簡介
關注於數據值的 ‘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庫交互的時候,也許我們也會上面的模式來描述一個完整的類型。
