星期六, 3月 29, 2014

TypeScript 界面(四)

上一篇談了 TypeScript 的函式,今天來聊聊 TypeScript 的界面(Interface),使用界面是撰寫物件導向程式中很入門磚,界面就像是對使用者訂立了契約,有定義就必須要實作(Implement),界面屬於高階的抽象,所以程式耦合性低,程式不會緊緊黏在一起,程式容易維護也不易出錯,TypeScript 提供了界面這個重要的特性,讓 JavaScript 可以用強型別的概念來撰寫程式,用例子來說明吧..

首先來個最簡單的例子

function Test(obj: { name: string }) {
    console.log(obj.name);
}

var myObj = { name: "Bibby" };
Test(myObj);

var myObj1 = { name: "Bibby", age: 18 };
Test(myObj1);

這個例子裡面,我限制 obj 一定要丟入一個有 name 屬性的一個物件,所以 myObj 跟 myObj1 都是可以當參數來丟進來的,上列的例子只是一種界面宣告的方法,TypeScript 有提供一個關鍵字來宣告界面,修改一下上面的例子

interface IPerson {
    name: string;
}

function Test(obj: IPerson) {
    console.log(obj.name);
}

var myObj = { name: "Bibby" };
Test(myObj);

var myObj1 = { name: "Bibby", age: 18 };
Test(myObj1);

使用 interface 的關鍵字,命名一個 IPerson 的界面,這樣可以做到跟上一個例子的一樣效果,不過注意一下,這裡的 myObj 跟 myObj1 只要屬性跟界面所要求的一樣就可以被允許,而不需要直接的實作(implement),這跟其他強型別的語言是不同的

可選屬性(Optional Properties)

interface IPerson {
    name: string;
    age?: number;
}

function Test(obj: IPerson) {
    console.log(obj.name);

    if (obj.age) {
        console.log(obj.age);
    }
}

var myObj = { name: "Bibby" };
Test(myObj);

var myObj1 = { name: "Bibby", age: 18 };
Test(myObj1);

看一下 IPerson 的 age 這個屬性,加個問號(?)代表這個屬性不一定要實作,這個特性可以讓你的界面設計更靈活,不過要小心點使用這個特性,所實作的程式也記得判斷是否有這屬性,個人建議是不要大量使用,可以考慮把 option 的 method 再拉一個界面出來來代替

函式(Function Type)

剛剛有提到,界面就像契約一樣,來看一下怎麼實作在函式上

interface IShow {
    (a1: string, a2: string): void;
}

var func: IShow = (a, b) => {
    console.log(a, b);
};

func("Bibby", "123");
func("Bibby", 123); // get errors

陣列(Array Type)

也可以實作在陣列上

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

interface INumberArray {
    [index: number]: number;
}

var myStringArray: IStringArray = ["Bibby", "Ruby"];
var myNumberArray: INumberArray = [123, 456];

型別(Class)

如果你有寫過物件導向的語言,這應該是最熟悉的用法了

interface IPerson {
    name: string;
    age: number;
    birthday: Date;
    print(): void;
}

class Person implements IPerson {

    name: string;
    age: number;
    birthday: Date;

    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    print() {
        console.log(this.name);
        console.log(this.age);
        console.log(this.birthday);
    }

}

var b = new Person("Bibby", 22);
b.birthday = new Date(2010, 10, 10);
b.print();

偷懶一點可以這樣寫

interface IPerson {
    name: string;
    age: number;
    birthday: Date;
    print(): void;
}

class Person implements IPerson {

    birthday: Date;

    constructor(public name: string, public age: number) { }

    print() {
        console.log(this.name);
        console.log(this.age);
        console.log(this.birthday);
    }
}

var b = new Person("Bibby", 22);
b.birthday = new Date(2010, 10, 10);
b.print();

用建構式的快速寫法把屬性宣告出來

延伸界面(Extending Interfaces)

在上面了例子,有提到把 option 的 method 再拉一個界面出來來代替,就是如下寫法

interface IColor {
    color: string;
}

interface ISolid {
    description: string;
}

interface ISquare extends ISolid {
    length: number;
}

class Square implements ISquare, IColor {
    description: string;
    length: number;
    color: string;
}

var square = new Square();
square.description = "red";
square.length = 10;

方法很簡單,就是用逗點(,)還實現多重實作的方式

最後提一下,如果你想要你的界面是不能被實作,可以用下面的方法

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

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

var clock: INotImplementInterface = Clock;
var newClock = new clock(7, 30);

這裡在 INotImplementInterface 用 new 這關鍵字來作為建構式的限制,也是這個原因就不能拿來實做了,但是還是可以拿來當約束界面用

TypeScript 的界面(interface)這部份就講到這了,下一篇來聚焦物件導向中,最好玩的類別(class) ^^

Reference
http://www.typescriptlang.org/
http://typescript.codeplex.com/wikipage?title=Interfaces%20in%20TypeScript&referringTitle=Documentation

2 則留言:

匿名 提到...

Bibby,你好我是人力資源顧問,昨天在TWMVC有跟大家交換名片. 我最近有再幫一些外商公司尋找這方面的人才,希望有機會可以認識你^^

Bibby 提到...

您好 ^^