为什么将联合类型赋予变量和将相同类型赋予参数如此不同?

Why are giving a union type to a variable and giving the same type to a parameter so different?

使用 tsc --strict 命令我得到以下错误:

error TS2339: Property 'foo' does not exist on type 'T'. Property 'foo' does not exist on type 'Bar'. 16 console.log(obj.foo)

我不明白的是为什么我可以将对象文字分配给 obj 但在函数内部使用相同的 属性 会出错。谢谢!

type Foo = {
  foo: string
  xyz: string
}

type Bar = {
  bar: string
  xyz: string
}

type T = Foo | Bar

let obj: T = { foo: "foo", xyz: "xyz" }

const sayHello = (obj: T) => {
  console.log(obj.foo)
}

当您分配给联合时,您可以分配联合中的任何一种构成类型。这就是赋值成功的原因

当您尝试访问联合类型的 parameter/variable 时,您实际上 不知道 哪个联合组成类型实际上包含在其中。它们可能会枯萎,因此 typescript 认为仅安全访问联合所有成员共有的属性。在您的示例中,访问 xyz 是安全的,因为它存在于两个联合成分中

您需要使用 type guard 才能使编译器将参数类型缩小为一种或另一种构成类型:


const sayHello = (obj: T) => {
  console.log(obj.xyz) // ok common 
  if ('bar' in obj) {
    obj.bar // ok
  } else {
    obj.foo //ok
  }
}

Playground Link

类型 Foo 比类型 Foo | Bar 更具体。如果你有一个可以放入 Foo | Bar 的盒子,那么你可以将 Foo 放入那个盒子,没问题。变量 obj 和同样命名为 obj 的参数在这方面是相同的;您可以将 FooBar 分配给变量,并且可以使用 FooBar 作为参数调用函数。

不同之处在于当您尝试从类型为 Foo | Barobj 获取 obj.foo 时。 .foo 属性 仅在 Foo 类型的事物上定义;但是参数 obj 不一定是 Foo,它可能是 Bar,所以它不一定有 .foo 属性。这就是您收到错误的原因。

但是,如果您尝试在函数外部使用类型为 T 的变量执行 console.log(obj.foo);,您将得到相同的错误。与是否为参数无关

在您的特定代码中,您没有看到此错误,因为即使您声明 obj 的类型为 T,Typescript 也知道您已将 Foo 分配给它,因此由于控制流类型缩小,变量在分配后实际上是 Foo 类型,并且在重新分配变量之前将保持原样。如果您更改声明以避免使用 as T 类型断言缩小,则错误将按预期出现:

let obj: T = { foo: "foo", xyz: "xyz" } as T
console.log(obj.foo); // type error