TypeScript - Interfaces


簡介

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM