TypeScript - Union Types
學習資源:
介紹
TypeScript 讓我們針對變數給予不同的型別。而通常若給予變數一個型別的話,我們就只允許給變數這個型別的值。
而另一方面,也可以給變數 any
型別,所以在 assign 值的時候就相對自由。
而有時候我們有可能會需要同時使用兩者。比方說,我們必須編寫一個程式來獲取員工的 ID,然後將 ID 印出到控制檯。問題是,員工的 ID 可以是字串或數字。 由於我們需要 ID 變數允許多個型別,因此我們可以使用 any
型別,例如:
let ID: any;
console.log(`The ID is ${ID}.`);
然而若使用 any
型別的話,TypeScript 就不會起到監督型別的作用。所以為了讓型別的註釋更彈性一些,才有了 Union 這個方式。
Defining Unions Types 定義聯合型別
Union 可以讓我們定義多個型別,以下是以上面的例子重新定義的樣子:
let ID: string | number;
// number
ID = 1;
// or string
ID = '001';
console.log(`The ID is ${ID}.`);
以上的例子可以看到可以看到ID 到型別可以允許字串跟數字。這樣的方式比較單一型別更為彈性,並且也比 any
更為嚴謹。
Union 也可以使用在函式的參數標記裡。這樣的方式很方便,因為函式常常會需要這種多元的 input 。
function getMarginLeft(margin: string | number) {
return { 'marginLeft': margin };
}
Type Narrowing
有了 Union 後,雖然型別註釋上更為彈性,但也還有一些事需要考慮。
function getMarginLeft(margin: string | number) {
// ...
}
比如上面的函式,他可以接受字串或是數字,所以這部分可能要針對這兩個型別做不同的邏輯處理。可以像下面的例子來做這樣的型別確認:
function getMarginLeft(margin: string | number) {
// margin may be a string or number here
if (typeof margin === 'string') {
// margin must be a string here
}
}
可以藉由上面的判斷式,來確認參數的型別,再做後續的處理,比如:
if (typeof margin === 'string') {
return margin.toLowerCase();
}
而這樣的概念就叫做 Type Narrowing,藉由使用 union 還有型別的邏輯判斷,讓我們在程式碼中縮小變數的類型範圍。
Inferred Union Return Types
TypeScript 一個好用的地方是,它能夠在許多情況下推斷型別,這樣我們就不必手動去編寫它們。一個很好的例子是函式的返回型別。TypeScript 會檢視函式的內容,並推斷該函式可能會返回哪些型別。如果有多個可能的返回型別,TypeScript 將推斷返回型別為 union。
以下面的例子為例,呼叫一個會失敗的函式 getBookFromServer()
:
function getBook() {
try {
return getBookFromServer();
} catch (error) {
return `Something went wrong: ${error}`;
}
}
若這個函式呼叫成功,他會返回一個 Book 型別。若失敗,會返回 string。而 TypeScript 可以藉由 getBook()
可以返回 Book 或 string 型別來做型別推斷,讓我們不用手動去標記。
Unions and Arrays
接下來會介紹當 Union 以及 Array 結合時優勢。
比如說,可以在 TypeScript 中用數字或字串型別表示時間。所以如果有兩種型別的日期列表,就需要一個允許字串和數字值的陣列。Unions 在這裡就會很有幫助。
const dateNumber = new Date().getTime(); // returns a number
const dateString = new Date().toString(); // returns a string
const timesList: (string | number)[] = [dateNumber, dateString];
以上面的例子,timesList
變數允許字串和數字型別作為其陣列中的值。所以如果我們嘗試向 timesList
新增一個不是 any
型別的值,比如 timesList.push(true)
,TypeScript 就會顯示一個錯誤。
Common Key Value Pairs
當使用 Union 方法時,TypeScript 只會允許使用兩個型別都可以共用的方法,比如:
const batteryStatus: boolean | number = false;
batteryStatus.toString(); // No TypeScript error
batteryStatus.toFixed(2); // TypeScript error
因為 toFixed()
只能給 number 使用,所以會出現錯誤。
而這樣的規則也同樣適用於 object。
type Goose = {
isPettable: boolean;
hasFeathers: boolean;
canThwartAPicnic: boolean;
}
type Moose = {
isPettable: boolean;
hasHoofs: boolean;
}
const pettingZooAnimal: Goose | Moose = { isPettable: true };
console.log(pettingZooAnimal.isPettable); // No TypeScript error
console.log(pettingZooAnimal.hasHoofs); // TypeScript error
Unions with Literal Types
我們也可以使用字面的型別來使用 Unions,讓我們需要針對狀態做定義的時候。
比如以下的例子,若我們想控制紅綠燈的狀態時:
type Color = 'green' | 'yellow' | 'red';
function changeLight(color: Color) {
// ...
}
這邊定義 changeLight()
接收的參數只能是紅綠燈的三個狀態,若丟入其他顏色就會產生錯誤,藉此做到狀態上的管控。並且也讓寫函式的時候可以減少一些意外地狀況。