TypeScript 高階型別

語言: CN / TW / HK

前面我們學的數字型別、字串型別、布林型別、陣列型別等等可能都是我們比較熟悉的資料型別,在其他計算機語言中也能經常見到。而本節我們要學習的是 TypeScript 中的高階型別,例如交叉型別、聯合型別、類型別名、字面量型別等。

交叉型別

交叉型別是將多個型別合併為一個型別。可以通過 & 來實現合併,生成的新型別包含了所需的所有型別的特性。

舉一個例子,假設 ABC 是三種不同的資料型別, 我們將三種類型合併為一個新的交叉型別 A & B & C ,那麼使用這個交叉型別宣告的物件可以同時包含 A、B、C 這個三種類型的成員。

一般交叉型別大多用於混入(mixins),或其他不適合典型面向物件模型的地方使用。在 JavaScript 裡發生這種情況的場合很多。

示例:

我們來看一個簡單的例子,下面 UserStudent 是我們定義好的兩個介面:

interface User {
    id: number,
    username: string,
    age: number
}
  
interface Student{
    id: number,
    score: number[],
}
  
let cross: User & Student;  // 交叉型別

// 變數cross擁有兩個介面中的所有屬性
cross.id;
cross.username;
cross.age;
cross.score;

交叉型別 User & Student 就包含了上述兩個介面中的所有屬性,而通過交叉型別宣告的變數 cross ,擁有兩個介面中的所有的屬性。

但是這種方式也需要注意一個問題,就是如果兩個介面中有相同的屬性,但是屬性的型別不同,這樣就會造成衝突,不可以賦值。例如:

聯合型別

聯合型別可以通過管道將變數設定多種型別,賦值時可以根據設定的型別來賦值。也就是說聯合型別的變數可以是指定的幾種資料型別中的一種,我們可以通過豎線 | 來分隔不同的型別。

語法格式:

Type1|Type2|Type3

需要注意的是,宣告為聯合型別的變數,只能賦值指定的型別的值,如果賦值其他型別的值也會報錯喲。

示例:

例如我們宣告一個聯合型別的變數 our

let our:number|string|boolean;

our = 18;
our = 'xkd';
our = true;

上述的程式碼表示變數 our 的值可以是 number 型別、 string 型別或者 boolean 型別中的一種。如果我們給變數 our 賦值為這三個指定資料型別以外的型別值,則會導致報錯喲。

類型別名

我們在使用型別時,可以通過 type 關鍵字為型別起一個別名,類型別名較多應用於聯合型別、交叉型別這種複合型別,這樣可以避免我們重複它們的定義。

起別名並不會新建一個型別,只是建立了一個新的名字來引用那個型別。

示例:

例如給一個自定義的聯合型別加上一個類型別名 admix ,然後我們在使用這個聯合型別來宣告變數的時候,就只需要指定變數為 admix 型別即可。這樣我們就不需要多次定義 number|string|boolean ,而直接使用別名 admix 來引用此型別就行了:

type admix = number|string|boolean;  // 給聯合型別加上別名

let a:admix = 100;
let b:admix = 'xkd';
let c:admix = true;

console.log(a);
console.log(b);
console.log(c);

編譯成 JavaScript 程式碼:

var a = 100;
var b = 'xkd';
var c = true;
console.log(a);
console.log(b);
console.log(c);

輸出:

100
xkd
true

字面量型別

TypeScript 中,字面量型別允許我們指定必須的固定值,通過分隔符 | 將不同字面量分隔,且也可以通過 type 關鍵字為自定義的字面量型別加上別名。

示例:

例如當變數的值為”小飛俠”就是“水星仔”其中之一,那我們就可以將這兩個值構成一個聯合型別字面量,:

type names = '小飛俠' | '水星仔';  

let a:names = '小飛俠';
let b:names = '水星仔';

上述程式碼中通過別名 names 將變數 ab 宣告為我們自定義的字面量,那麼這兩個變數的值僅能為“小飛俠” 或者 “水星仔” 其中之一,否則會報錯。

一般我們是很少直接這樣使用的,在一些特殊情況下,例如使用字串字面量型別來區分函式過載,或用於縮小範圍除錯 bug 等,可以這樣使用。

訪問聯合型別的屬性或者方法

TypeScript 不確定一個聯合型別的變數到底是哪個型別的時候,我們只能訪問此聯合型別的所有型別裡共有的屬性或方法。

示例:

例如我們在一個函式 info 中定義兩個引數,引數 username 的型別為 string 型別,而 age 的型別為一個聯合型別,可以是 string 型別或 number 型別:

function info( username: string, age : string | number) {
    console.log(age.length);  // 報錯
}

我們在使用 age 來訪問屬性 length 時會導致報錯,這是因為只有字串型別中才有屬性 length ,數字型別沒有這個屬性。 由於 length 並不是數字型別和字串型別的共有屬性,所以被宣告為這個聯合型別的變數 age 不能訪問這個屬性。

function info( username: string, age : string | number) {
    console.log(age.toString());  
}

通過 age 來訪問 toString() 方法不會報錯,這是因為 toString() 方法是數字型別和字串型別共有的方法,所以被宣告為聯合型別的 age 可以訪問這個方法。