为什么 TypeScript 无法正确推断类型?

Why is TypeScript unable to infer the type properly?

我有两个例子,其中 TypeScript 类型推断没有像我预期的那样工作,也许这两个是相关的,也许是 TS 错误,也许我太笨了,无法理解它。

示例 #1

interface AType {
  type: 'A'
  message: string
}

interface BCType {
  type: 'B' | 'C'
}

type Test = AType | BCType

function doSomething(test: Test) {
  if (test.type !== 'B' && test.type !== 'C') {
    test.message
  }
}

test.message获取错误

Property 'message' does not exist on type 'Test'.
  Property 'message' does not exist on type 'BCType'.ts(2339)

为什么 AType 现在不是 test,而是 message 属性?如果我从类型中删除 'C' ,它就可以工作。如果我检查 === 'A',也是有效的。

示例 #2

interface AType {
  type: {
    id: 'A'
  }
  message: string
}

interface BType {
  type: {
    id: 'B'
  }
}

type Test = AType | BType

function doSomething(test: Test) {
  if (test.type.id === 'A') {
    test.message
  }
}

这里也一样:

Property 'message' does not exist on type 'Test'.
  Property 'message' does not exist on type 'BType'.ts(2339)

类型保护通常作用于它们所应用的变量 field,因此如果您缩小 test.type,缩小通常只适用于 type 而不是 test.一个例外是如果 test 的类型是一个可区分的联合并且 test 是一个可区分的字段。

要使 属性 成为判别式 属性,它 must 满足一些要求:

  1. 属性 是此处概述的文字类型 Discriminated union types
  2. 联合类型的 属性 是判别式 属性 如果它的联合类型包含至少一个单元类型并且没有此处概述的可实例化类型 Allow non-unit types in union discriminants

至于第二个示例,只是不支持嵌套可区分联合。有一个proposal支持这个但是已经开了3年了,没什么动静。

您可以调整您的模型以符合有区别的联合规则,或者作为变通方法,您可以使用自定义类型保护:

interface AType {
  type: 'A'
  message: string
}

interface BCType {
  type: 'B' | 'C'
}

type Test = AType | BCType

function doSomething(test: Test) {
  if (!isBC(test)) {
    test.message
  }
}

function isBC(o: any): o is BCType {
  return o?.type != "B" && o?.type != "C"
}

Playground Link