繁体   English   中英

如何区分已区分的联合体类型

[英]How to discriminate a discriminated union type

假设我有一个区分联合类型来表示Redux动作:

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: Action )太笼统了。 我想说“对应于P typeAction ”,但我不知道它是否存在。 我尝试了Action & {type: P}但是却相反。

有任何想法吗?

更新,2018年7月

自从我写了这个答案以来,TypeScript 2.8引入了条件类型 ,这使得这成为可能。

例如,在这种情况下:

type DiscriminateAction<T extends Action['type']> = Extract<Action, {type: T}>

其中Extract<T, U>来自标准库条件类型,定义为:

type Extract<T, U> = T extends U ? T : never;

它使用条件类型的分布属性来拆分联合T并仅拉出与U匹配的那些部分。

定义ActionReducers方法如下:

type ActionReducers = {
  [P in Action['type']]: (state: State, action: DiscriminateAction<P>) => State
};

所以,那行得通! 希望对人们有所帮助。


原答复,2017年7月

TypeScript不允许您自动查找带标记的联合的类型。 这是个好主意,所以您可能要提个建议 该逻辑已作为控制流分析的一部分实现; 也许可以将其公开为某种类型的运算符。


如果没有此功能,则有解决方法。 最直接的方法是自己声明反向映射,然后在需要时引用它,但要花一些时间:

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

希望其中一项为您服务。 祝好运!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM