1. 程式人生 > >typescript物件、陣列、函式的型別詳解

typescript物件、陣列、函式的型別詳解

一、物件的型別:介面

概述:在面嚮物件語言中,介面(Interfaces)是一個很重要的概念,它是對行為的抽象,而具體如何行動需要由類(classes)去實現(implements)

1.介面使用

這個例子中定義了一個介面 Person,接著定義了一個變數 tom,它的型別是 Person。這樣,我們就約束了 tom 的形狀必須和介面 Person 一致

//正確使用
interface Person { name: string; age: number; }
let tom: Person = { name: 'Tom', age: 25 };

// 錯誤案例一:定義的變數的屬性比介面少
interface Person { name: string; age: number; }
let tom: Person = { name: 'Tom' }; // error : Property 'age' is missing in type '{ name: string; }'

// 錯誤案例二:定義的變數的屬性比介面多
interface Person { name: string; age: number; }
let tom: Person = { name: 'Tom', age: 25, gender: 'male' }; // error: 'gender' does not exist in type 'Person'.

結論:賦值的時候,變數的形狀必須和介面的形狀保持一致

2.可選屬性:

//可選屬性案例一:正確使用
interface Person { name: string; age?: number; }
let tom: Person = { name: 'Tom' }; // ok 
let tony: Person = { name: Tony, age: 25}; // ok 

//可選屬性案例二:錯誤使用
interface Person { name: string; age?: number; }
let tom: Person = { name: 'Tom', age: 25, gender: 'male' }; //error: 'gender' does not exist in type 'Person'

結論:可選屬性的含義是該屬性可以不存在,但仍然不允許新增未定義的屬性

3.任意屬性:

//任意屬性案例一:正確使用
//使用 [propName: string] 定義了任意屬性取 string 型別的值
interface Person { name: string; age?: number; [propName: string]: any; }
let tom: Person = { name: 'Tom', gender: 'male' };

//任意屬性使用:錯誤使用
interface Person {
    name: string;
age?: number; // error:型別'number'的屬性'age'不能分配給字串索引型別'string',型別'number'不能分配給'string'型別
[propName: string]: string;
}

let tom: Person = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};

結論:一旦在介面中定義了任意屬性,那麼確定其中和它並列的屬性和可選屬性都必須是它的子屬性

4.只讀屬性:

概述:有時候我們希望物件中的一些欄位只能在建立的時候被賦值,那麼可以用 readonly 定義只讀屬性

// 錯誤案例使用一:
interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

let tom: Person = {
    id: 89757,
    name: 'Tom',
    gender: 'male'
};

tom.id = 9527; // error: 無法分配給'id',因為它是常量或只讀屬性

//錯誤案例使用二:

interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any;
}

// 在賦值的時候,屬性id需要被初始化
// error1:  Property 'id' is missing in type '{ name: string; gender: string; }
let tom: Person = {
    name: 'Tom',
    gender: 'male'
};

tom.id = 89757; //error2: Cannot assign to 'id' because it is a constant or a read-only property

結論(注意):只讀的約束存在於第一次給物件賦值的時候,而不是第一次給只讀屬性賦值的時候

二、陣列的型別

1.「型別 + 方括號」表示法

//ok(注意:陣列的每一項型別必須保持一致)
let fibonacci: number[] = [1, 1, 2, 3, 5];

//error: Type '(string | number)[]' is not assignable to type 'number[]'
let fibonacci: number[] = [1, '1', 2, 3, 5];

//error: Argument of type '"8"' is not assignable to parameter of type 'number'.
let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push('8');

2.陣列泛型表示法

//ok (其注意事項與上面的基本表示保持一致)
let fibonacci: Array<number> = [1, 2, 2, 3, 5];
fibonacci.unshift(10);

3.陣列介面表示法

// NumberArray 表示:只要 index 的型別是 number,那麼值的型別必須是 number
interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];

4.any在陣列中的運用

let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com' }];

5.類陣列:arguments、NodeList、HTMLCollection等

function sum() {
    let args: NodeList = document.getElementsByTagName('a');
}

三、函式的型別(* 核心)

1.函式的宣告(包括函式宣告和函式表示式)

//函式宣告
function sum(x: number, y: number): number {
    return x + y;
}

//函式表示式
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

批註:(1)輸入多餘的(或者少於要求的)引數,是不被允許的(2)在 TypeScript 的型別定義中,=> 用來表示函式的定義,左邊是輸入型別,需要用括號括起來,右邊是輸出型別。

2.使用介面定義函式的形狀

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

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

3.定義函式可選引數

function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

批註:可選引數必須接在必需引數後面。換句話說,可選引數後面不允許再出現必須引數了。

4.設定函式引數預設值

function buildName(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');

5.多個不確定剩餘引數

function push(array: any[], ...items: any[]) {
    items.forEach(function(item) {
        array.push(item);
    });
}

let a = [];
push(a, 1, 2, 3);

6.函式的過載

function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''));
    } else if (typeof x === 'string') {
        return x.split('').reverse().join('');
    }
}

批註:上例中,我們重複定義了多次函式 reverse,前幾次都是函式定義,最後一次是函式實現。在編輯器的程式碼提示中,可以正確的看到前兩個提示。TypeScript 會優先從最前面的函式定義開始匹配,所以多個函式定義如果有包含關係,需要優先把精確的定義寫在前面。