使用Redux(这个问题与Redux没有任何关系)我希望得到一个reducer使用的动作的名称,但我想确保reducer中使用的名称和动作对应。所以我写了这段代码:
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"
?
答案 0 :(得分: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 argument
必须是string
或number
,因为它是返回对象中的一个键。所以我制作了ActionType扩展字符串,然后剩下的就好了。