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,剩下的部分就沒問題了。
