如何区分歧视的工会类型

时间:2017-02-01 00:03:14

标签: typescript

让我们说我有一个歧视的联合类型代表Redux的行动:

dynamic

如果我想为处理它们的Reducer制作一个动作类型的地图,我可以从:

开始
interface AddTodoAction { type: 'ADD_TODO'; description: string; }
interface RemoveTodoAction { type: 'REMOVE_TODO'; id: number; }
type Action = AddTodoAction | RemoveTodoAction;

但是,第二个参数(type ActionReducers = { [P in Action['type']]: (state: State, action: Action) => State }; )过于笼统。我想说action: ActionAction对应type",但我不知道它是否存在。我试过P,但那种情况正好相反。

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

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'];

希望其中一件适合你。祝你好运!