首頁 >web前端 >js教程 >TypeScript 中介面詳解_javascript技巧

TypeScript 中介面詳解_javascript技巧

WBOY
WBOY原創
2016-05-16 15:54:071561瀏覽

在 TypeScript 中,介面是用來當約束作用的,在編譯成 JavaScript 的時候,所有的介面都會被擦除掉,因為 JavaScript 中並沒有介面這個概念。

先看一個簡單的例子:

function printLabel(labelledObj: { label: string }) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

那麼在這個方法中,labelledObj 的類型就是{label: string},看上去可能有點複雜,但我們看見看看下面myObj 的聲明就知道,這是聲明了一個擁有size 屬性(值為10)和label 屬性(值為"Size 10 Object")的物件。所以方法參數 labelledObj 的型別是 {label: string} 即表示參數擁有一個 string 類型的 label 屬性。

但是,這麼寫的話,這個方法看起來還是有點讓人糊塗。那就可以用介面(interface)來定義這個方法的參數類型。

interface LabelledValue {
  label: string;
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label);
}

var myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

可選屬性
有些時候,我們並不需要屬性一定存在,就可以使用可選屬性這個特性來定義。

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" });

那麼我們就傳入了實作一個 SquareConfig 介面的物件入 createSquare 方法。

既然完全是可有可無的,那為什麼還要定義呢?對比起完全不定義,定義可選屬性有兩個優點。 1.如果存在屬性,能約束類型,這是十分關鍵的;2、能得到語法智能提示,假如誤將方法體中 color 寫成 collor,那麼編譯是不通過的。

方法類型

在 JavaScript 中,方法 function 是一種基本型別。在物件導向思想中,介面的實作是靠類別來完成的,而 function 作為一種類型,是不是能夠實作介面呢?答案是肯定的。

在 TypeScript 中,我們可以使用介面來約束方法的簽章。

interface SearchFunc {
 (source: string, subString: string): boolean;
}

var mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
 var result = source.search(subString);
 if (result == -1) {
  return false;
 }
 else {
  return true;
 }
}

上面程式碼中,我們定義了一個接口,接口內約束了一個方法的簽名,這個方法有兩個字串參數,傳回布林值。在第二段程式碼中我們聲明了這個介面的實作。

要注意的是,編譯器只是檢查型別是否正確(參數型別、回傳值型別),因此參數的名字我們可以換成別的。

var mySearch: SearchFunc;
mySearch = function(src: string, sub: string) {
 var result = src.search(sub);
 if (result == -1) {
  return false;
 }
 else {
  return true;
 }
}

這樣也是能夠編譯通過的。

陣列類型
在上面我們在介面中定義了方法類型,那麼,數組類型又該如何定義呢?很簡單。

interface StringArray {
 [index: number]: string;
}

var myArray: StringArray;
myArray = ["Bob", "Fred"];

那麼 myArray 就是一個數組,而且索引器是 number 類型,元素是 string。

在介面的定義裡面,索引器的名字一般為 index(當然也可以改成別的,但一般情況下都是保持名字為 index)。所以改成

interface StringArray {
 [myIndex: number]: string;
}

var myArray: StringArray;
myArray = ["Bob", "Fred"];

也是 ok 的。

要注意的是,索引器的型別只能為 number 或 string。

interface Array{
  [index: number]: any;
}

interface Dictionary{
  [index: string]: any;
}

上面兩段都是可以編譯通過的。

最後還有一點要注意的是,如果介面已經是數組類型的話,介面中定義的其它屬性的類型都必須是該數組的元素類型。例如:

interface Dictionary {
 [index: string]: string;
 length: number;  // error, the type of 'length' is not a subtype of the indexer
}

那麼將無法編譯通過,需要將 length 改成 string 類型才可以。

使用類別實作介面
一般情況下,我們還是習慣使用一個類,實現需要的接口,而不是像上面直接用接口。

interface ClockInterface {
  currentTime: Date;
}

class Clock implements ClockInterface {
  currentTime: Date;
  constructor(h: number, m: number) { }
}

在 TypeScript 中,使用 class 關鍵字來宣告了,這跟 EcmaScript 6 是一樣的。

另外,我們可以使用介面來約束類別中定義的方法。

interface ClockInterface {
  currentTime: Date;
  setTime(d: Date);
}

class Clock implements ClockInterface {
  currentTime: Date;
  setTime(d: Date) {
    this.currentTime = d;
  }
  constructor(h: number, m: number) { }
}

在 TypeScript 中,我們可以為介面定義建構子。

interface ClockInterface {
  new (hour: number, minute: number);
}

接下來天真的我們可能會接著這麼寫:

interface ClockInterface {
  new (hour: number, minute: number);
}

class Clock implements ClockInterface {
  currentTime: Date;
  constructor(h: number, m: number) { }
}

這是不行的! ! !因為建構子是 static(靜態)的,而類別只能夠實作介面中的 instance(實例)部分。

那麼這個介面中定義的建構子豈不是沒作用?既然 TypeScript 提供了這項功能,那麼肯定不會是沒作用的。聲明的方法比較特殊:

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);

正常情況下我們是寫 new Clock 的,這裡就將 Clock 類別指向了 ClockStatic 介面。需要注意的是,newClock 變數的類型是 any。

繼承介面
像類別一樣,介面也能實現繼承,使用的是 extends 關鍵字。

interface Shape {
  color: string;
}

interface Square extends Shape {
  sideLength: number;
}

var square = <Square>{};
square.color = "blue";
square.sideLength = 10;

當然也能繼承多個介面。

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 Shape {
  color: string;
  test: number;
}

interface PenStroke {
  penWidth: number;
  test: string;
}

interface Square extends Shape, PenStroke {
  sideLength: number;
}

那么这段代码就无法编译通过了,因为 test 属性的类型无法确定。

同时使用上面所述的类型
如果仅能单一使用某种类型,那么这接口也未免太弱了。但幸运的是,我们的接口很强大。

interface Counter {
  (start: number): string;
  interval: number;
  reset(): void;
}

var c: Counter;
c(10);
c.reset();
c.interval = 5.0;

这样就使用到三种类型了,分别是方法(接口自己是个方法)、属性、方法(定义了方法成员)。

以上所述就是本文的全部内容了,希望大家能够喜欢。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn