跳至主要内容

宽度子类型

如果对象类型是 不精确的,则可以使用具有“额外”属性的对象在标注为特定属性集的位置使用。

1function func(obj: {foo: string, ...}) {2  // ...3}4
5func({6  foo: "test", // Works!7  bar: 42      // Works!8});

func 中,我们知道 obj 至少有一个属性 foo,并且属性访问表达式 obj.foo 的类型为 string

这是一种通常称为“宽度子类型”的子类型,因为“更宽”(即具有更多属性)的类型是更窄类型的子类型。

因此,在以下示例中,obj2obj1子类型

1let obj1: {foo: string, ...}  = {foo: 'test'};2let obj2 = {foo: 'test', bar: 42};3obj2 as {foo: string, ...};

但是,通常需要知道某个属性肯定不存在。

1function func(obj: {foo: string, ...} | {bar: number, ...}) {2  if (obj.foo) {3    obj.foo as string; // Error!
4 }5}
3:5-3:11: Cannot cast `obj.foo` to string because property `foo` of unknown type [1] is incompatible with string [2]. [incompatible-cast]

上面的代码存在类型错误,因为 Flow 也允许调用表达式 func({foo: 1, bar: 2}),因为 {foo: number, bar: number}{bar: number, ...} 的子类型,它是参数联合类型的成员之一。

对于需要断言属性不存在的情况,可以使用 精确对象类型

1function func(obj: {foo: string} | {bar: number}) {2  if (obj.foo) {3    obj.foo as string; // Works!4  }5}

精确对象类型 禁用宽度子类型,并且不允许存在其他属性。

使用精确对象类型可以让 Flow 知道运行时不会存在其他属性,这允许 细化 变得更加具体。