TS _介面
- 介紹
TypeScript的核心原則之一是對值所具有的結構進行型別檢查。 它有時被稱做“鴨式辨型法”或“結構性子型別化”。 在TypeScript裡,介面的作用就是為這些型別命名和為你的程式碼或第三方程式碼定義契約。
- 介面初探
下面通過一個簡單示例來觀察介面是如何工作的:
function printLabel(labelledObj: { label: string }) { console.log(labelledObj.label); } let myObj = { size: 10, label: "Size 10 object" }; printLabel(myObj);
型別檢查器會檢視printLabel
的呼叫。printLabel
有一個引數,並要求這個物件引數有一個名為label
型別為string
的屬性。 需要注意的是,我們傳入的物件引數實際上會包含很多屬性,但是編譯器只會檢查那些必需的屬性是否存在,並且其型別是否匹配。 然而,有些時候TypeScript卻並不會這麼寬鬆,我們下面會稍做講解。
下面我們重寫上面的例子,這次使用介面來描述:必須包含一個label
屬性且型別為string
:
interface LabelledValue{ label: string; } function printLabel(labelledObj: LabelledValue) { console.log(labelledObj.label) } let myObj= { size: 10, label: 'size 10 object' }; printLabel(myObj);
LabelledValue
介面就好比一個名字,用來描述上面例子裡的要求。 它代表了有一個label
屬性且型別為string
的物件。 需要注意的是,我們在這裡並不能像在其它語言裡一樣,說傳給printLabel
的物件實現了這個介面。我們只會去關注值的外形。 只要傳入的物件滿足上面提到的必要條件,那麼它就是被允許的。
還有一點值得提的是,型別檢查器不會去檢查屬性的順序,只要相應的屬性存在並且型別也是對的就可以。
- 可選屬性
接口裡的屬性不全都是必需的。 有些是隻在某些條件下存在,或者根本不存在。 可選屬性在應用“option bags”模式時很常用,即給函式傳入的引數物件中只有部分屬性賦值了。
下面是應用了“option bags”的例子:
interface SquareConfig{ color?: string; width?: number; } function createSquare(config: SquareConfig): { color:string; area:number }{ let newSquare = { color: 'white', area: 100 }; if (config.color) { newSquare.color = config.color; } if (config.width) { newSquare.area = config.width * config.width; } return newSquare; } let mySquare = createSquare({color:'black'})
帶有可選屬性的介面與普通的介面定義差不多,只是在可選屬性名字定義的後面加一個?
符號。
可選屬性的好處之一是可以對可能存在的屬性進行預定義,好處之二是可以捕獲引用不存在的屬性時的錯誤。 比如,我們故意將createSquare
裡的color
屬性名拼錯,就會得到一個錯誤提示:
// Error: Property 'clor' does not exist on type 'SquareConfig'
- 只讀屬性
一些物件屬性只能在物件剛剛建立的時候修改其值。 你可以在屬性名前用readonly
來指定只讀屬性:
interface Point{
readonly x: number;
readonly y: number;
}
你可以通過賦值一個物件字面量來構造一個Point
。 賦值後,x
和y
再也不能被改變了。
let p1: Point = { x: 10, y: 20 }; p1.x = 5; // error!
TypeScript具有ReadonlyArray<T>
型別,它與Array<T>
相似,只是把所有可變方法去掉了,因此可以確保陣列建立後再也不能被修改:
let a: number[] = [1, 2, 3, 4]; let ro: ReadonlyArray<number> = a; ro[0] = 12; // error! ro.push(5); // error! ro.length = 100; // error! a = ro; // error!
上面程式碼的最後一行,可以看到就算把整個ReadonlyArray
賦值到一個普通陣列也是不可以的。 但是你可以用型別斷言重寫:
let a: number[] = [1, 2, 3, 4]; let ro: ReadonlyArray<number> = [22,33,44]; // a = ro; // error! a = ro as number[]; //output a = [22,33,44]