1. 程式人生 > >TypeScript與JavaScript不同之處系列(二) ===> 介面

TypeScript與JavaScript不同之處系列(二) ===> 介面

本系列目的: 列出TypeScript與JavaScript的不同點, 縮小文件內容, 提高學習速度. 原文件地址: https://www.tslang.cn/index.html

物件

最簡單的使用例子

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"}); let myError = createSquare({color: "black", gkd: '1'}); // 報錯

額外檢查

上面例子可以看到如果, 傳入gkd

屬性就會報錯, 那麼如何繞開這些錯誤呢.

// 1. 使用型別斷言(型別斷言參見下面的介紹)
let mySquare = createSquare({ width: 100, gkd: '1'} as SquareConfig);

// 2. 增加檢驗屬性
interface SquareConfig {
    color?: string;
    width?: number;
    [propName: string]: any;
}

官方建議, 在這裡,如果支援傳入gkd屬性到createSquare,你應該修改SquareConfig定義來體現出這一點。

型別斷言

通過型別斷言這種方式可以告訴編譯器,“相信我,我知道自己在幹什麼”。 型別斷言好比其它語言裡的型別轉換,但是不進行特殊的資料檢查和解構。 它沒有執行時的影響,只是在編譯階段起作用。 TypeScript會假設你,程式設計師,已經進行了必須的檢查。

型別斷言有兩種形式。 其一是“尖括號”語法:

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;

// 另一個為as語法:

let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;

兩種形式是等價的。 至於使用哪個大多數情況下是憑個人喜好;然而,當你在TypeScript裡使用JSX時,只有 as語法斷言是被允許的


只讀屬性

一些物件屬性只能在物件剛剛建立的時候修改其值。 你可以在屬性名前用 readonly來指定只讀屬性:

interface Point {
    readonly x: number;
    readonly y: number;
}

let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!, 因為Point只讀

TypeScript具有ReadonlyArray<T>型別,它與Array<T>相似,只是把所有可變方法去掉了,因此可以確保陣列建立後再也不能被修改:

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!

// 可以用型別斷言重寫:
a = ro as number[];

可索引型別

它描述了物件索引的型別,還有相應的索引返回值型別, 例:

interface StringArray {
  [index: string]: number;
  length: number;    // 可以,length是number型別
  name: string;       // 錯誤,`name`的型別與索引型別返回值的型別不匹配
}

let myArray: StringArray;
myArray['gkd'] = 123;

let myStr: number = myArray['gkd'];

函式

為了使用介面表示函式型別,我們需要給介面定義一個呼叫簽名。 它就像是一個只有引數列表和返回值型別的函式定義。引數列表裡的每個引數都需要名字和型別

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

//對於函式型別的型別檢查來說,函式的引數名不需要與接口裡定義的名字相匹配。

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

//  如果你不想指定型別也可以這麼寫
let mySearch: SearchFunc;
mySearch = function(src, sub) {
    let result = src.search(sub);
    return result > -1;
}

類型別

類型別按照js話來講, 就是物件.

簡單使用

// 例1

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

interface PenStroke {
    penWidth: number;
}

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

let square = <Square>{}; // 可以理解為Square型別的物件
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;

square.gkd= 5.0; // 報錯 Property 'gkd' does not exist on type 'Square'.

混合型別

為JavaScript其動態靈活的特點,有時你會希望一個物件可以同時具有上面提到的多種型別。一個例子就是,一個物件可以同時做為函式和物件使用,並帶有額外的屬性.

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

function getCounter(): Counter {
    let counter = <Counter>function (start: number) { };
    counter.interval = 123;
    counter.reset = function () { };
    return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

介面繼承類

當介面繼承了一個類型別時,它會繼承類的成員但不包括其實現。 就好像介面聲明瞭所有類中存在的成員,但並沒有提供具體實現一樣。 介面同樣會繼承到類的private和protected成員

class Control {
    private state: any;
}

interface SelectableControl extends Control {
    select(): void;
}

class Button extends Control implements SelectableControl {
    select() { }
}