網站搜尋

TypeScript - 字串字面值型別參數未被強制執行


使用 Redux(不過這個問題其實與 Redux 本身沒有特別關係)我想要取得 reducer 使用的 action 的名稱,但我想確保名稱與 reducer 中使用的 action 相符。所以我寫了這段程式碼:

interface TypedAction<N> {
  type: N;
}

type TypedReducer<S, A extends TypedAction<any>> = (state: S, action: A) => S;

function addReducer<S, R extends TypedReducer<S, A>, A extends TypedAction<N>, N>(initialState: S, actionName: N, reducer: R): {} {
  // doesn't really matter what is returned here
  // the point is I need the actionName during run time
  // but want it to correspond with the reducer's action's name at compile time
  return {
    [actionName.toString()]: reducer
  };
}

但是當我嘗試一個例子時:

interface MyAction extends TypedAction<'MyAction'> {
  foo: string;
}

const myActionReducer: TypedReducer<number, MyAction> = (state: number, action: MyAction) => state+1;

addReducer(1, "foo", myActionReducer); // should give a compile error, because "foo" and is not of type "MyAction"

為什麼 Typescript 不強制要求 "foo" 應該是 "MyAction"?

答案#1

interface TypedAction<T extends string> {
  type: T;
}

type TypedReducer<S, A extends TypedAction<any>> = (state: S, action: A) => S;

interface MyAction extends TypedAction<"MyAction"> {
  foo: number;
}

type ActionTypeAndReducer<S, A extends TypedAction<any>> = {
  [type: string]: TypedReducer<S, A>
};

function pair<ActionType extends string,
              A extends TypedAction<ActionType>,
              S>(type: ActionType, reducer: TypedReducer<S, A>): ActionTypeAndReducer<S, A> {
  return {
    [type as string]: reducer
  };
}

const myReducer: TypedReducer<any, MyAction> = (state: any, action: MyAction) => {};
pair("MyAction2", myReducer);

這將產生預期的行為。

error TS2345: 
   Argument of type 'TypedReducer<any, MyAction>' is not assignable to parameter
   of type 'TypedReducer<any, TypedAction<"MyAction2">>'.

   Types of parameters 'action' and 'action' are incompatible.                                                                                                                           
   Type 'TypedAction<"MyAction2">' is not assignable to type 'MyAction'.                                                                                                             
   Property 'foo' is missing in type 'TypedAction<"MyAction2">'.                                                                                                                 

我原本以為一個結合了 action 和 reducer 的函數可以檢查這個,所以我建立了 pair 函數。類型是沒問題的,但編譯器抱怨說 type 參數 必須是 string 或 number,因為它是返回物件中的一個鍵。 所以我讓 ActionType 繼承自 string,剩下的部分就沒問題了。