交叉類型
交叉類型的表示方法為 Type1 & Type2,結果是取這兩個類型的並集。這里是官網的例子,做了注釋:
// 定義函數 extend,用來合並對象 function extend<T, U>(first: T, second: U): T & U { // result 是要返回結果,類型斷言為 T & U let result = {} as T & U // 遍歷 first,結果存入 result for(let id in first){ // 不能將類型“T”分配給類型“T & U”,所以需使用斷言 result[id] = first[id] as any } // 遍歷 second,結果存入 result for(let id in second){ if(!result.hasOwnProperty(id)){ // 不能將類型“U”分配給類型“T & U”,同樣需要斷言 result[id] = second[id] as any } } // 返回結果,類型是 T & U return result } // 定義 Person 類,有 name 實例屬性,類型為 string class Person{ constructor(public name: string){} } // 定義接口,要求有 log() 函數 interface Loggable{ log(): void } // 定義 ConsoleLogger 類,它實現了接口 Loggable,有實例方法 log() class ConsoleLogger implements Loggable{ log(){ } } // 使用 extend 方法合並兩個類的實例,返回的是交叉類型,所以可以訪問 name 和 log() let jim = extend(new Person('Jim'), new ConsoleLogger()) let n = jim.name jim.log()
例子中的結果可以看到,交叉類型取的是並集,擁有兩個類型成員的所有屬性。
聯合類型
聯合類型的表示方法為 Type1 | Type2,結果是這兩個類型中的一個。還是以官網例子做解釋:
function PadLeft(value: string, padding: any){ // 如果是 number 類型,則在 value 前填充對用空格 if(typeof padding === 'number'){ return Array(padding + 1).join(' ') + value } // 如果是 string 類型,則直接拼接 value 和 padding if(typeof padding === 'string'){ return padding + value } // 如果不是 string 和 number,拋出錯誤 throw new Error(`Expected string or number, got '${padding}'.`) } PadLeft("hello world", 4) // ' hello world'
上述代碼可以通過編譯,能正常運行,但是有一個問題,就是 padding 的類型,當 padding 傳入的既不是 number 也不是 string 時,雖然運行結果會拋出錯誤,但是在編譯階段不會提示問題:
padLeft("Hello world", true); // 編譯階段通過,運行時報錯
要在編譯階段就可以知道問題,可以使用聯合類型來替代 any 類型:
function PadLeft(value: string, padding: string | number){ // ... } PadLeft("hello world", true) // error,類型“true”的參數不能賦給類型“string | number”的參數。
聯合類型表示一個值可以是幾種類型之一。 我們用豎線( |
)分隔每個類型,所以 number | string | boolean
表示一個值可以是 number
, string
,或 boolean
。
需要注意的是,訪問聯合類型的屬性時,只能訪問此聯合類型的所有類型里共有的屬性:
let a: string | number a.length // 類型“string | number”上不存在屬性“length”。類型“number”上不存在屬性“length”。
length 不存在於 number,所以編譯不通過。可以看出這跟交叉類型不一樣,交叉類型是可以訪問所有成員的屬性。
總結
交叉類型是多個類型合並為一個類型,可以訪問所有類型的屬性;聯合類型是多個類型中的某一個,只能訪問所有類型的共有屬性。