typescript 中的 interface

有關接口概念之前的 javascript 新方向中我們提及到了 javascript 作爲一門動態語言本身沒有接口的概念。但是接口好處是不言而喻,javascript 要是想幹大事就少不了接口。在 javascript 中接口是定義。
我們的第一個接口,函數 printLabel 接受參數,對參數有一定要求,也就是參數對象需要有 label 屬性。當然傳入的對象參數可以除了有 label 屬性也可以有其他屬性,但是必須至少有一個 label 屬性。

第一個接口

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

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

我們定義 interface 接口 LabelledValue ,主要來定義一個數據結構,然後將參數類型定義爲 LabelledValue ,這樣就確保 printLabel 函數接受參數爲一個至少具有 label 屬性的對象。這樣顯式定義一個具有 label 的對象。

interface LabelledValue {
    label: string;
}

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

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

可選屬性

當然我們定義接口,有時候沒有必要實現每一個屬性,
這裏定義的 SquareConfig 接口與之前定義接口區別不大,不過在屬性名稱後面多了 ? 來表示該屬性爲可選屬性。

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 函數,接受一個 SquareConfig 接口的對象,這裏 SquareConfig 定義數據結構,但是數據結構中每一個屬性不是必須而是可選的,然後返回一個 {color: string; area: number} 數據結構。

只讀屬性

多數情況下,創建的實現某個接口的對象,是可以在創建後對對象屬性進行修改的,這就是 mutable ,我們在現代編程需要 ,特別是函數式編程中需要 immutable ,我們可以在接口定義時在屬性(字段)屬性前面添加修飾 readonly

interface Point {
    readonly x: number;
    readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!
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!

附加屬性檢查

之前我們已經學習到了可選屬性定義,我們定義 createSquare 參數類型爲 SquareConfig,以及返回類型

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
    // ...
}

let mySquare = createSquare({ colour: "red", width: 100 });

如果我們傳入的參數屬性名稱可接口不一致,在編譯時就會提示錯誤。獲取我們傳入一個 interface 中沒有定義的類型同樣會報編譯錯誤。

// error: 'colour' not expected in type 'SquareConfig'
let mySquare = createSquare({ colour: "red", width: 100 });
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);

但是我們通過定義一個附加屬性就可以輕鬆地解決這個問題 [propName: string]: any;

interface SquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}
let squareOptions = { colour: "red", width: 100 };
let mySquare = createSquare(squareOptions);

定義函數類型

之前我們學習的接口通常用於描述 javascript 對象的結構(shape),接口還可以描述一個函數。

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

這裏我們定義接口 SearchFunc 用於描述一個函數,接受字符串類型 source 和字符串類型 subString 返回一個 boolean 類型

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    let result = source.search(subString);
    return result > -1;
}

好我們定義了變量接受一個函數作爲值。爲這個 SearchFunc 類型變量賦值函數也必須具有接口 SearchFunc 定義的結構。

接口的繼承

有關接口的繼承,在 typescript 接口是可以繼承,所謂繼承可能解釋爲擴展可能更好理解

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

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

接口的多繼承

我們定義接口可以繼承多個接口,這裏 square 繼承了 shape 和 Penstroke 兩個接口,然後具有自己的屬性。

interface Shape {
    color: string;
}

interface PenStroke {
    penWidth: number;
}

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

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章