TypeScript快覽

丁巨集宇發表於2018-08-05

一. 資料型別

變數可以使用資料型別修飾, 限制了變數別隨意賦值帶來的不可預測性

let age: number = 12;
let isDone: boolean = false;
let myName: string = 'Tom';
let unusable: void = undefined;
let u: undefined = undefined;
let n: null = null;
複製程式碼

有的時候資料型別是不可預測的, 有的場景下, 也是需要可以接受任意型別的變數. 這時候就可以使用任意值

let anyThing: any = 'hello';
anyThing = 3;
複製程式碼

型別推斷

  • 在宣告時,給定了值, 那麼變數就會被推斷對應的型別.
let myFavoriteNumber = 'seven'; // let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;
複製程式碼
  • 如果定義的時候沒有賦值,不管之後有沒有賦值,都會被推斷成 any 型別而完全不被型別檢查
let something; // let something: any;

something = 'seven';
something = 7;

something.setName('Tom');
複製程式碼

聯合型別

  • 一個型別可以同時指定多個型別
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
複製程式碼

當使用聯合型別時, 變數只能使用兩個型別中公共的方法屬性.

  • 如果是我們需要在方法中根據型別呼叫特定的方法時, 就需要使用斷言語法. 斷言只能從聯合型別中選取型別

複製程式碼

物件型別---介面

程式開發有一個很流行的模式,就是面向介面程式設計. 介面抽象了結構, 真正的實現就可以提供多種多樣的能力.

interface Person {
    name: string; // name是確定屬性, 實現介面的物件,必須失效該屬性
    age?: number; // age是可選屬性, 也就是真正實現時,不需一定實現.
    readonly id: number; // 只讀屬性, 

}

let tom: Person = {
    name: 'Tom',
    age: 25,
    id: 100, // 只能在物件賦值時設定值. 
};
複製程式碼

陣列的型別

let fibonacci: number[] = [1, 1, 2, 3, 5];

// 報錯, 陣列中只允許單一型別
let fibonacci2: number[] = [1, '1', 2, 3, 5];

// 泛型陣列
let fibonacci: Array<number> = [1, 1, 2, 3, 5]; 

// 介面型別陣列
interface NumberArray {
    [index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5]; 

// 要求陣列中存放不同型別的值
let list: any[] = ['Xcat Liu', 25, { website: 'http://xcatliu.com' }];



複製程式碼

函式的型別

  • 注意,輸入多餘的(或者少於要求的)引數,是不被允許的
function sum(x: number, y: number): number {
    return x + y;
}

sum(1, 2); // 正常
sum(1, 2, 3);// 多輸入一個引數, 會報錯
複製程式碼
  • 使用可選引數, 可以省略引數的輸入, 可選引數一定要在引數列表的尾部.
function buildName(firstName: string, lastName?: string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        return firstName;
    }
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');	
複製程式碼
  • 用介面定義函式的型別
interface SearchFunc {
   (source: string, subString: string): boolean;
}

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

宣告檔案: declare

我們需要使用 declare 關鍵字來定義它的型別,幫助 TypeScript 判斷我們傳入的引數型別對不對:

declare var jQuery: (selector: string) => any;

jQuery('#foo');
複製程式碼

注意:

  • 通常宣告檔案放到一個單獨的檔案中管理. 例如: jQuery.d.ts

  • 用「三斜線指令」表示引用了宣告檔案, /// <reference path="./jQuery.d.ts" />

  • 第三方宣告檔案, npm install @types/jquery --save-dev

Node

Node.js 不是內建物件的一部分,如果想用 TypeScript 寫 Node.js,則需要引入第三方宣告檔案:

npm install @types/node --save-dev
複製程式碼

二. 進階用法

關鍵字: type

型別別名

例如定義一個函式的型別, 引數型別加上返回值型別, 導致型別描述很長, 這時候就可以使用型別別名簡化.

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === 'string') {
        return n;
    }
    else {
        return n();
    }
}
複製程式碼

字串字面量型別

作為型別時, 引數只能是約定好的內容. 類似於列舉

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}

handleEvent(document.getElementById('hello'), 'scroll');  // 沒問題
handleEvent(document.getElementById('world'), 'dbclick'); // 報錯,event 不能為 'dbclick'
複製程式碼

元祖

陣列: 相同元素集合,

元祖: 不同元素集合

let xcatliu: [string, number];
xcatliu[0] = 'Xcat Liu';
xcatliu[1] = 25;

xcatliu[0].slice(1);
xcatliu[1].toFixed(2);

// 報錯, 型別不一致
let k: [string, number] = [18,'Tom', 18]

複製程式碼

注意:

  • 元素的定義時的位置是固定的. [18,'Tom', 18] 的型別就是[number, string, number]
  • 對應位置可以使用該位置型別的方法,
  • 如果在元祖中push
  • 額外元素的型別為元祖中元素的聯合型別 number | string

關鍵字: enum

一般列舉

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
複製程式碼

編譯後

var Days;
(function (Days) {
    Days[Days["Sun"] = 0] = "Sun";
    Days[Days["Mon"] = 1] = "Mon";
    Days[Days["Tue"] = 2] = "Tue";
    Days[Days["Wed"] = 3] = "Wed";
    Days[Days["Thu"] = 4] = "Thu";
    Days[Days["Fri"] = 5] = "Fri";
    Days[Days["Sat"] = 6] = "Sat";
})(Days || (Days = {}));
;
複製程式碼
  • 它的預設值,如下每個較前一個 + 1
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true


console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true
複製程式碼
  • 手動賦值, 以最後面的數值遞增.
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
複製程式碼

常數列舉

每一項的內容是常數, 不可計算得到.

const enum Directions {
  Up,
  Down,
  Left,
  Right
}

let dir: Directions = Directions.Up
複製程式碼

編譯後

var dir = 0 /* Up */;
複製程式碼

外部列舉

// 此處必須使用const, 不然declare 定義的型別只會用於編譯時的檢查,編譯結果中會被刪除。
declare const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
複製程式碼

編譯結果:

var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
複製程式碼

三. 類的使用

訪問修飾符

  • public : 任何地方都可以被訪問, 預設的屬性和方法都是publick
  • private: 只有當前類可以訪問.
  • protected: 和private類似, 但可以被子類訪問.

抽象類

abstract 用於定義抽象類和其中的抽象方法.

使用關鍵字 extends 繼承抽象類

注意:

  • 抽象類不能被例項化
  • 抽象方法必須被子類實現

介面

介面可以描述一個物件屬性, 同時可以對行為進行抽象

  • 使用關鍵字 implements 實現介面
  • 一個類可以實現多個介面
  • 介面之間可以是繼承關係
interface Alarm {
    alert();
}
interface Light {
    lightOn();
    lightOff();
}
interface FlyableAlarm extends Alarm{
    fly();
}
class Door {
}

class SecurityDoor extends Door implements Alarm {
    alert() {
        console.log('SecurityDoor alert');
    }
}
class Car implements Alarm, Light {
    alert() {
        console.log('Car alert');
    }
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off');
    }
}
class Plane implements FlyableAlarm{
    alert(){
        console.log('Plane alert');
    }
    fly(){
        console.log('Plane fly');
    }
}
複製程式碼
  • 介面繼承類
class Point {
    x: number;
    y: number;
}

interface Point3d extends Point {
    z: number;
}

let point3d: Point3d = {x: 1, y: 2, z: 3};
複製程式碼

泛型

定義函式,介面或類的時候, 不預先指定具體的型別, 而在使用的時候再指定型別的一種特性

  • 函式使用泛型
function createArray<T,N extends number>(length: N, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray<string>(3, 'x'); // ['x', 'x', 'x']
複製程式碼
  • 介面使用泛型
interface CreateArrayFunc {
    <T>(length: number, value: T): Array<T>;
}
// 也可以把泛型提升到介面名上
//interface CreateArrayFunc<T> {
//    (length: number, value: T): Array<T>;
//}

let createArray: CreateArrayFunc;
createArray = function<T>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

createArray(3, 'x'); // ['x', 'x', 'x']
複製程式碼
  • 使用泛型
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
複製程式碼
  • 指定泛型的預設型別
function createArray<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
複製程式碼

原文連線: https://ts.xcatliu.com/

相關文章