我正在使用打字稿,正在研究大量从redux工作方式(action, state) => state
借用的代码。
以尽可能严格的方式尝试使用打字稿。我的问题最好用这个例子来解释。
首先,我有一个列举动作类型的枚举:
enum Tags {
TAG_A = 'tagA',
TAG_B = 'tagB'
}
接下来,我定义每种类型的唯一字段:
type Payload = {
[Tags.TAG_A]: {
foo: string;
},
[Tags.TAG_B]: {
bar: number;
}
}
现在我定义我的操作类型:
type Action<T extends Tags> = {
type: T;
id: string;
moreCommonField: boolean;
} & Payload[T];
type Actions = { [T in Tags]: Action<T> }[Tags]
现在是减速器:
type State = { somevar: string }; //doesnt matter, example
const reducer = (action: Actions, state: State): State => {
switch (action.type) {
case Tags.TAG_A:
action.foo; /* correct type, yay */
break;
case Tags.TAG_B:
action.bar; /* correct type, yay */
break;
}
return {...state};
}
到目前为止,一切都很好!
但是在现实世界中,我有很多动作,而我的switch
看起来很丑。
所以我尝试了这个:
const minireducers: { [K in Tags]: (action: Action<K>, state: State) => State } = {
[Tags.TAG_A]: (action, state) => {
// we have correct type of action here, big benefit
return {...state};
},
[Tags.TAG_B]: (action, state) => ({...state})
}
这很棒,因为大多数“微型减速器”只是一个衬套。这还具有迫使我不要忘记处理以后可能添加的任何操作的好处。
但是,当我尝试为此编写实际的reducer时:
const reducer2 = (action: Actions, state: State) => {
const minireducer = minireducers[action.type];
const newState = minireducer(action, state); //error
return newState;
}
这里的错误对我很清楚,action.type
没有缩小为任何特定的类型,因此minireducer
可以是很多东西,它的类型是联合,您不能称其为联合。使用强制转换显然可以正常工作,但是我想要一个不需要强制转换的解决方案。甚至只是一个思想实验。
任何人都对尽可能安全的解决方案有任何想法,这将允许我将减速器拆分为较小的减速器,如示例中所示?
您不必坚持我的类型定义或结构,但是这里的目标还是要确保类型安全和良好的类型推断。
答案 0 :(得分:1)
问题在于minireducer
将是所有签名的并集,并且该签名不可调用。
有feature的打字稿3.3
可以放宽此限制,但是您的代码将无法正常工作,因为这将期望第一个参数是签名并集中所有参数的交集。
一种解决方案,如果您不介意进行额外的函数调用,则可以使用额外的函数,这会使编译器休息,使该对象具有正确的签名:
const reducer2 = (action: Actions, state: State) => {
// r must have a property K that is a function taking Action<K>
function call<K extends Tags>(o: Action<K>, r: Record<K, (action: Action<K>, state: State) => State>, state: State) {
r[o.type](o, state); // r[o.type] is (action: Action<K>, state: State) => State which is callable with an Action<K>
}
const newState = call(action, minireducers, state);
return newState;
}