宽度子类型
如果对象类型是 不精确的,则可以使用具有“额外”属性的对象在标注为特定属性集的位置使用。
1function func(obj: {foo: string, ...}) {2 // ...3}4
5func({6 foo: "test", // Works!7 bar: 42 // Works!8});
在 func
中,我们知道 obj
至少有一个属性 foo
,并且属性访问表达式 obj.foo
的类型为 string
。
这是一种通常称为“宽度子类型”的子类型,因为“更宽”(即具有更多属性)的类型是更窄类型的子类型。
因此,在以下示例中,obj2
是 obj1
的子类型。
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 知道运行时不会存在其他属性,这允许 细化 变得更加具体。