typescript:将联合类型“缩小”为其组件之一

uinbv5nw  于 2023-01-14  发布在  TypeScript
关注(0)|答案(2)|浏览(159)

假设我有这样的联合类型:

type Foo = {tag: 'A', name: string} | {tag: 'B', value: number}

我试着写一个函数来获取一个标签值('A' | 'B'),然后在Foo列表中进行一些特定于业务的查找,并返回一个匹配的元素。为了回答这个问题,我们可以考虑这个非常简单的函数:

function findFirst(foos: Foo[], tag: 'A' | 'B') {
  for (const foo of foos) {
    if (foo.tag === tag) { 
      return foo 
    }
  }
  return undefined
}

假设我现在调用这个函数,传递'A'作为第二个参数,很明显,它返回undefined{tag: 'A', name: string}对象,在这种情况下,它不能返回{tag: 'B', value: number}
尽管如此,TSC似乎无法推断出这一点。当编写以下表达式时:

findFirst([{tag: 'A', name: 'X'}], 'A')?.name

TSC错误为Property 'name' does not exist on type 'Foo'.
我猜我需要通过显式定义findFirst()的返回类型来帮助编译器推断正确的方法,但我不确定如何定义。
(Playground)

63lcw9qa

63lcw9qa1#

您需要向函数添加一个type参数,以捕获传入的标记的实际类型。然后,您需要使用Extract预定义的条件类型过滤Foo联合:

function findFirst<T extends Foo['tag']>(foos: Foo[], tag: T) {
  for (const foo of foos) {
    if (foo.tag === tag) { 
      return foo as Extract<Foo, { tag: T }>
    }
  }
  return undefined
}

Playground链接

h5qlskok

h5qlskok2#

可以使用函数重载声明结果,而不必使用强制转换。

type FooA = {tag: 'A', name: string};
type FooB = {tag: 'B', value: number};

type Foo = FooA | FooB 

function findFirst(foos: Foo[], tag: 'A'): FooA | undefined;
function findFirst(foos: Foo[], tag: 'B'): FooB | undefined;
function findFirst(foos: Foo[], tag: 'A' | 'B') {
  for (const foo of foos) {
    if (foo.tag === tag) { 
      return foo 
    }
  }
  return undefined
}

findFirst([{tag: 'A', name: 'X'}], 'A')?.name
findFirst([{tag: 'A', name: 'X'}], 'B')?.value

// TS Error as expected
findFirst([{tag: 'A', name: 'X'}], 'A')?.value

Playground链接

相关问题