簡介
關注於數據值的 ‘shape’的類型檢查是TypeScript核心設計原則。這種模式有時被稱為‘鴨子類型’或者‘結構子類型化’。 。 在TypeScript中接口interfaces的責任就是命名這些類型,而且還是你的代碼之間或者是與外部項目代碼的契約。
初見Interface
理解interface的最好辦法,就是寫個hello world程序:
function printLabel(labelledObj: {label: string}) { console.log(labelledObj.label); } var myObj = {size: 10, label: "Size 10 Object"}; printLabel(myObj);
在這里類型檢查系統會檢查printLabel這個函數,printLabel函數要求傳入一個包含一個label的字符串屬性。上例可以了解我們傳入的對象可以有多個屬性,但是類型檢查系統只會檢查printLabel所要求的label屬性是否滿足其要求。
接下來我們可以進一步簡化,聲明一個interface來描述一個具有label字符串屬性的對象:
interface LabelledValue { label: string; } function printLabel(labelledObj: LabelledValue) { console.log(labelledObj.label); } var myObj = {size: 10, label: "Size 10 Object"}; printLabel(myObj);
接口LabelledValue描述了上例中printLabel的所要求的類型對象。它依然代表包含一個label的字符串屬性。可以看見我們利用‘shape’(行)描述了printLabel的傳入參數要求。
可選的Properties
有時不是所有定義在interface中的屬性都是必須的。例如流行的”option bags”模式(option配置),使用者可以之傳入部分定制化屬性。如下面“option bags”模式:
interface SquareConfig { color?: string; width?: number; } function createSquare(config: SquareConfig): {color: string; area: number} { var newSquare = {color: "white", area: 100}; if (config.color) { newSquare.color = config.color; } if (config.width) { newSquare.area = config.width * config.width; } return newSquare; } var mySquare = createSquare({color: "black"});
帶有可選屬性的interface定義和c#語言很相似,以’?‘緊跟類型后邊表示。
interface的可選屬性可以限制那些屬性是可用的,這部分能得到類型檢查,以及智能感知。例如下例:
interface SquareConfig { color?: string; width?: number; } function createSquare(config: SquareConfig): {color: string; area: number} { var newSquare = {color: "white", area: 100}; if (config.color) { newSquare.color = config.collor; // 類型檢查系統能識別不正確的屬性collor. } if (config.width) { newSquare.area = config.width * config.width; } return newSquare; } var mySquare = createSquare({color: "black"});
函數類型
Interfaces為了描述對象能接收的數據形(shapes)的返回。對於interface不僅難呢過描述對象的屬性,也能描述函數類型。
下面是定義的interface signature是一個接收兩個string的輸入參數,並返回boolean的函數類型:
interface SearchFunc { (source: string, subString: string): boolean; }
我也可以使用函數類型的interface去描述我們的數據。下面演示如何將一個相同類型的函數賦值給interface:
var mySearch: SearchFunc; mySearch = function(source: string, subString: string) { var result = source.search(subString); if (result == -1) { return false; } else { return true; } }
對於函數類型的interface,並不需要參數名的對應相同,如下例:
var mySearch: SearchFunc; mySearch = function(src: string, sub: string) { var result = src.search(sub); if (result == -1) { return false; } else { return true; } }
對於函數參數的檢查,會按照參數的順序檢查對應的類型是否匹配。同時也會檢查函數的返回類型是否匹配,在這個函數我們期望返回boolean類型true/false, 如果返回的是numbers或者string,則類型檢查系統會提示返回值類型不匹配。
數組類型
類似於函數類型,TypeScript也可以描述數組類型。在數組類型中有一個’index’類型其描述數組下標的類型,以及返回值類型描述每項的類型,如下:
interface StringArray { [index: number]: string; } var myArray: StringArray; myArray = ["Bob", "Fred"]
index的支持兩種類型,分別是字符串和數值類型. 我們可以限制index的類型,同時也可以檢查index項的返回值類型。
index的類型簽名可以描述常用的數組或者是‘dictionary’(字典序)模式,這點會強制所有的屬性都需要和其返回值匹配。下例中length屬性不匹配這點,所以類型檢查會給出一個錯誤:
interface Dictionary { [index: string]: string; length: number; // error, the type of 'length' is not a subtype of the indexer }
Class類型
實現interface
在C#和java中interface是很常使用的類型系統,其用來強制其實現類符合其契約。在TypeScript中同樣也可以實現:
interface ClockInterface { currentTime: Date; } class Clock implements ClockInterface { currentTime: Date; constructor(h: number, m: number) { } }
同樣也可以在interface中實現函數類型的契約,並要求clas實現此函數。如下例的‘setTime’函數:
interface ClockInterface { currentTime: Date; setTime(d: Date); } class Clock implements ClockInterface { currentTime: Date; setTime(d: Date) { this.currentTime = d; } constructor(h: number, m: number) { } }
interface描述的的是class的公開(public)部分,而不是私有部分.
類static/instance區別
當使用類和接口的時候,我們需要知道類有兩種類型:static(靜態)部分和instance(實例)部分。如果嘗試一個類去實現一個帶有構造簽名的interface,TypeScript類型檢查會提示你錯誤。
interface ClockInterface { new (hour: number, minute: number); } class Clock implements ClockInterface { currentTime: Date; constructor(h: number, m: number) { } }
這是因為一個類去實現接口的時候,只有instance(實例)的部分才會被檢查。而構造函數屬於靜態部分,所以不會被類型檢查。
相反你可以直接在類中實現這些(static)靜態部分,如下例:
interface ClockStatic { new (hour: number, minute: number); } class Clock { currentTime: Date; constructor(h: number, m: number) { } } var cs: ClockStatic = Clock; var newClock = new cs(7, 30);
interface的繼承
和類一樣,接口也能集成其他的接口。這相當於復制接口的所有成員。接口的集成是的我們可以自由的抽象和分離到可重用的組件。
interface Shape { color: string; } interface Square extends Shape { sideLength: number; } var square = <Square>{}; square.color = "blue"; square.sideLength = 10;
一個interface可以同時集成多個interface,實現多個接口成員的合並。
interface Shape { color: string; } interface PenStroke { penWidth: number; } interface Square extends Shape, PenStroke { sideLength: number; } var square = <Square>{}; square.color = "blue"; square.sideLength = 10; square.penWidth = 5.0;
混合式類型
如前邊提到的一樣,在interface幾乎可以描述JavaScript世界的一切對象。因為JavaScript是一個動態,靈活的腳本語言,所以偶爾也希望一個對象能夠描述一些多個類型.
下面是一個混合式描述函數,對象以及額外屬性的實例:
interface Counter { (start: number): string; interval: number; reset(): void; } var c: Counter; c(10); c.reset(); c.interval = 5.0;
和第三方JavaScript庫交互的時候,也許我們也會上面的模式來描述一個完整的類型。