typescript - 如何区分受歧视的联合类型

标签 typescript

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

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

如果我想将 Action 类型映射到处理它们的缩减器,我可能会从:

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

但是,第二个参数 (action: Action) 过于笼统。我想说“与P对应的类型Action”,但我不知道它是否存在。我尝试了 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'];

希望其中一个对您有用。祝你好运!

关于typescript - 如何区分受歧视的联合类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41969749/

相关文章:

javascript - Eslint 警告不允许我推送到仓库

typescript - 在 Visual Studio Code 中创建没有语言服务器的诊断条目

reactjs - ant design 3.0 版表单上的 Typescript 错误

javascript - 这个 `implicitly` 有类型 any 它没有类型注释吗?

javascript - 即使不在 console.log 中,值也为 null (...)

javascript - 对象的差异?

typescript - 为什么 tsconfig.json 在 src 文件夹中找不到我的文件?

Angular 5 + SignalR (DefinitelyTyped) - 找不到名称 '$'

typescript - 在 prop 中传播值时出现 MUI 5 SX Typescript 错误

typescript - 使用 d3.scaleQuantile 创建色标时,类型 'string' 不可分配给类型 'number'