寬度子類型
如果物件類型是 不精確 的話,在註解為特定屬性集的位置使用具有「額外」屬性的物件是安全的。
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, ...} 的子類型,而 {bar: number, ...} 是參數聯合類型的一個成員。
對於像這樣需要斷言屬性不存在的情況,你可以使用 精確物件類型。
1function func(obj: {foo: string} | {bar: number}) {2 if (obj.foo) {3 obj.foo as string; // Works!4 }5}精確物件類型 會停用寬度子類型,而且不允許存在其他屬性。
使用精確的物件類型讓 Flow 知道在執行階段不會存在額外的屬性,這允許精煉變得更具體。