typescript - 确保 TypeScript 联合在整个代码中保持狭窄

标签 typescript

我有一个流程,在该流程中,我不想添加大量 if 语句,而是创建一个保存所需信息的对象,以便在执行时以更线性的方式读取。我在下面创建了一个简单且人为的示例。

type ItemOne = {
  type: 'itemOne'
  fn: typeof funcOne
  args: Parameters<typeof funcOne>[0]
}

type ItemTwo = {
  type: 'itemTwo',
  fn: typeof funcTwo
  args: Parameters<typeof funcTwo>[0]
}

type Item = ItemOne | ItemTwo


function funcOne({ name }: { name: string }) {
  console.log(name)
}

function funcTwo({ age }: { age: number }) {
  console.log(age)
}

function createItem(shouldCreateItemOne: boolean): Item {
  if (shouldCreateItemOne) {
    return { type: 'itemOne', fn: funcOne, args: { name: 'Dave' } } as const
  }

  return { type: 'itemTwo', fn: funcTwo, args: { age: 22  } } as const
}


const item = createItem(false) 

item.fn(item.args)

当我开始调用函数item.fn(item.args)时,TypeScript 提示Argument of type '{ name: string; } | { 年龄:数字; }' 不可分配给类型为 '{ name: string; 的参数; } & { 年龄:数字; }'. 有没有办法确保 TypeScript 可以保留 item.args 的缩小版本?

TypeScript playground 中还有一个示例链接

最佳答案

这看起来像是 overloads 的工作,您希望返回类型根据给定参数的类型进行更改:

function createItem(shouldCreateItemOne: true): ItemOne;
function createItem(shouldCreateItemOne: false): ItemTwo;
function createItem(shouldCreateItemOne: boolean): Item;
function createItem(shouldCreateItemOne: boolean): Item {
  if (shouldCreateItemOne) {
    return { type: 'itemOne', fn: funcOne, args: { name: 'Dave' } } as const
  }

  return { type: 'itemTwo', fn: funcTwo, args: { age: 22  } } as const
}

如果您不希望支持使用通用 boolean 类型调用函数,则最后一个重载是可选的。使用重载允许 TypeScript 推断出此调用的正确类型:

const item = createItem(false)
//    ^? ItemTwo
item.fn(item.args) // okay

Playground


如果您有两个以上的项目并且需要更大的灵活性,那么这可能不是最适合您的。请参阅@jcalz's comment使用通用索引访问的替代方案。


您还可以将 Item 类型重构到 map 中:

interface ItemMap {
  itemOne: typeof funcOne;
  itemTwo: typeof funcTwo;
}

type Item<K extends keyof ItemMap = keyof ItemMap> = {
  [P in K]: { type: P; fn: ItemMap[P]; args: Parameters<ItemMap[P]>[0] };
}[K];

createItem 声明现在看起来像

function createItem(shouldCreateItemOne: true): Item<"itemOne">;
function createItem(shouldCreateItemOne: false): Item<"itemTwo">;
function createItem(shouldCreateItemOne: boolean): Item;
function createItem(shouldCreateItemOne: boolean): Item {

Playground

关于typescript - 确保 TypeScript 联合在整个代码中保持狭窄,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75794155/

相关文章:

javascript - 使用严格模式在 TypeScript 中找不到对象时应该返回什么?

html - mat-table 不会被来自restservice 的数据填充

javascript - 获取具有特定类的每个 "rect"的 x Position 属性

node.js - 结合 Typescript Koa-Router 和 Passport

css - 如何将 CSS 模块的类型声明编写为 ES 模块

angular - Webpack 从模块导入的类不是构造函数

javascript - 如果一个对象结构与另一个对象结构不匹配/不匹配,如何抛出异常

typescript - 分隔联合类型的每个成员

javascript - 无法将值绑定(bind)到 setter 中 p-autocomplete 中的 ngModel

android - 从 TypeScript 调用 Android 方法