假设我有一个有区别的联合类型来代表Redux动作:
interface AddTodoAction { type: 'ADD_TODO'; description: string; }
interface RemoveTodoAction { type: 'REMOVE_TODO'; id: number; }
type Action = AddTodoAction | RemoveTodoAction;
如果我想为处理它们的Reducer制作一个动作类型的映射,我可以从:
type ActionReducers = {
[P in Action['type']]: (state: State, action: Action) => State
};
但是,第二个参数(action:Action)太笼统了.我想说“与P对应的类型的动作”,但我不知道它是否存在.我试过Action& {type:P}但是那种情况正好相反.
有任何想法吗?
最佳答案 更新,2018年7月
自从我写这个答案以来,TypeScript 2.8引入了conditional types,这使得这成为可能.
例如,在这种情况下:
type DiscriminateAction<T extends Action['type']> = Extract<Action, {type: T}>
其中Extract< T,U>是conditional type from the standard library定义为:
type Extract<T, U> = T extends U ? T : never;
它使用条件类型的distributive属性来拆分并集T并仅拉出与U匹配的那些部分.
以下是ActionReducers的定义方式:
type ActionReducers = {
[P in Action['type']]: (state: State, action: DiscriminateAction<P>) => State
};
所以,这有效!希望能帮助别人.
原始答案,2017年7月
TypeScript不允许您自动查找标记联合的类型.这是一个很好的想法,所以你可能想要make a suggestion.逻辑已经作为控制流分析的一部分实现了;也许它可以作为某种类型的运算符公开.
如果没有此功能,则有解决方法.最简单的方法就是自己声明反向映射,然后在需要时引用它,代价是重复:
type ActionMapping = {
ADD_TODO: AddTodoAction;
REMOVE_TODO: RemoveTodoAction;
}
interface Action { type: keyof ActionMapping }; // useful for consistency
interface AddTodoAction extends Action {
type: 'ADD_TODO'; // manually cross-reference
description: string;
}
interface RemoveTodoAction extends Action {
type: 'REMOVE_TODO'; // manually cross-reference
id: number;
}
// if you want more Action types you need to add it to ActionMapping:
interface BadAction extends Action {
type: 'BAD'; // error, BadAction incorrectly extends Action
title: string;
}
现在您可以定义您想要的内容:
type ActionReducers = {
[P in keyof ActionMapping]: (state: State, action: ActionMapping[P]) => State
};
这是另一种减少重复的方法,但更复杂:
// define all your data types here without the type property
type ActionDataMapping = {
ADD_TODO: { description: string },
REMOVE_TODO: { id: number }
}
// the ActionMapping programmatically adds the right tag to the data
type ActionMapping = {
[K in keyof ActionDataMapping]: ActionDataMapping[K] & { type: K };
}
// and an Action is just the union of values of ActionMapping properties
type Action = ActionMapping[keyof ActionMapping];
// this is the same as above
type ActionReducers = {
[P in keyof ActionMapping]: (state: State, action: ActionMapping[P]) => State
};
一切都应该在这里工作.你的Action子类型缺少漂亮的名字.如果你愿意,可以添加它们,但它有点复制:
// if you need names for them:
type AddTodoAction = ActionMapping['ADD_TODO'];
type RemoveTodoAction = ActionMapping['REMOVE_TODO'];
希望其中一件适合你.祝好运!