在下面的示例中,如何为action
中的withoutSwitchReducer
参数提供正确的键入?
enum ActionTypesEnum {
FOO = 'FOO',
BAR = 'BAR',
}
type ActionTypes = {
type: ActionTypesEnum.FOO,
payload: { username: string }
} | {
type: ActionTypesEnum.BAR,
payload: { password: string },
};
// "withSwitchReducer" works fine as TS can infer the descriminator from action.type
function withSwitchReducer(action: ActionTypes) {
switch (action.type) {
case 'FOO':
return action.payload.username;
case 'BAR':
return action.payload.password;
default:
return null;
}
}
// The code below gives as error as "action.payload.username" is not available on "action.payload.password" and vice versa
const withoutSwitchReducer = {
[ActionTypesEnum.FOO]: (action: ActionTypes) => {
return action.payload.username;
},
[ActionTypesEnum.BAR]: (action: ActionTypes) => {
return action.payload.password;
}
};
此处具有Intellisense的相同代码:TS Playground Link
答案 0 :(得分:0)
有两种方法可以做到这一点。
您可以一次声明类型:
const withoutSwitchReducer: { [k in ActionTypesEnum]: (action: Extract<ActionTypes, { type: k }>) => string } = {
[ActionTypesEnum.FOO]: (action) => {
return action.payload.username;
},
[ActionTypesEnum.BAR]: (action) => {
return action.payload.password;
},
};
或者您可以单独描述它们:
const withoutSwitchReducer2 = {
[ActionTypesEnum.FOO]: (action: Extract<ActionTypes, { type: ActionTypesEnum.FOO }>) => {
return action.payload.username;
},
[ActionTypesEnum.BAR]: (action: Extract<ActionTypes, { type: ActionTypesEnum.BAR }>) => {
return action.payload.password;
},
};
一次声明类型的好处很明显,您不必一遍又一遍地做相同的事情,但是单独描述它们可以使您从推断这些函数的返回类型中受益。
更新:正如Titian Cernicova-Dragomir在评论中提到的那样,您可以将其声明为要在其他地方重用的类型:
type ReducerMap<T extends { type: string }> = { [P in T['type']]: (action: Extract<T, { type: P }>) => any }
该函数的返回类型为any
。
我试图找到一种方法来推断每个定义的实际返回值,但这不太可能。
并且由于它被用作化简器映射,因此您可能不会在乎框架所消耗的返回值。
答案 1 :(得分:0)
ActionTypes
是复合类型。实例化变量时,应使用as
明确指出其特定类型。
function withSwitchReducer(action: ActionTypes) {
switch (action.type) {
case 'FOO':
return (action.payload as {
type: ActionTypesEnum.FOO,
payload: { username: string }
}).username;
case 'BAR':
return (action.payload as {
type: ActionTypesEnum.BAR,
payload: { password: string }
}).password;
default:
return null;
}
}
const withoutSwitchReducer = {
[ActionTypesEnum.FOO]: (action: ActionTypes) => {
return (action.payload as {
type: ActionTypesEnum.FOO,
payload: { username: string }
}).username;
},
[ActionTypesEnum.BAR]: (action: ActionTypes) => {
return (action.payload as {
type: ActionTypesEnum.BAR,
payload: { password: string }
}).password;
}
};
interface Foo {
type: 'FOO'
payload: { username: string }
}
interface Bar {
type: 'BAR'
payload: { password: string }
}
type ActionTypes = Foo | Bar
function withSwitchReducer(action: ActionTypes) {
switch (action.type) {
case 'FOO':
return (action.payload as Foo).username;
case 'BAR':
return (action.payload as Bar).password;
default:
return null;
}
}
const withoutSwitchReducer = {
'FOO': (action: ActionTypes) => {
return (action.payload as Foo).username;
},
'BAR': (action: ActionTypes) => {
return (action.payload as Bar).password;
}
};
字符串文字可以用作类型,例如var a: 'Apple'
。您可以将它们组合在一起,例如var b: 'Apple' | 'Orange'
。