TypeScript學習摘要(八)——高級類型

交叉類型(Intersection Types)

  • 某個類型的對象同時擁有多種類型的成員,交叉類型使用&創建

    function extend<T, U>(first: T, second: U): T & U {
        let result = <T & U>{};
        for (let id in first) {
            (<any>result)[id] = (<any>first)[id];
        }
        for (let id in second) {
            if (!result.hasOwnProperty(id)) {
                (<any>result)[id] = (<any>second)[id];
            }
        }
        return result;
    }
    class Person {
        constructor(public name: string) { }
    }
    interface Loggable {
        log(): void;
    }
    class ConsoleLogger implements Loggable {
        log() {
            // ...
        }
    }
    var jim = extend(new Person("Jim"), new ConsoleLogger());
    var n = jim.name;
    jim.log();
    

聯合類型(Union Types)

  • 與交叉類型對應,聯合類型表示一個值可以是幾種類型之一,用|表示

  • 如果一個值是聯合類型,我們只能訪問此聯合類型的所有類型里共有的成員

    interface Bird {
        fly();
        layEggs();
    }
    
    interface Fish {
        swim();
        layEggs();
    }
    
    function getSmallPet(): Fish | Bird {
        // ...
    }
    
    let pet = getSmallPet();
    pet.layEggs(); // okay
    pet.swim();    // errors
    

類型保護

用戶自定義類型保護

  • 由于聯合類型中,我們只能訪問所有類型共有的成員,想要確切的判斷是否為某個類型只能反復使用斷言

    let pet = getSmallPet();
    
    if ((<Fish>pet).swim) {
        (<Fish>pet).swim();
    }
    else {
        (<Bird>pet).fly();
    }
    

    可以通過定義類型保護避免反復斷言

    function isFish(pet: Fish | Bird): pet is Fish {
        return (<Fish>pet).swim !== undefined;
    }
    // 返回值pet is Fish 是一個類型謂詞
    // pet為函數參數,Fish是聯合類型的成員之一
    // 調用一次類型保護后,TypeScript會將變量縮減為那個具體的類型
    if (isFish(pet)) {
        pet.swim();
    }
    else {
        pet.fly();
    }
    

typeof 類型保護(基礎類型保護)

  • 只有兩種形式typeof v === "typename"typeof v !== "typename"
  • "typename"必須是 "number""string""boolean""symbol",與其它字符串比較不會被識別為類型保護

instanceof類型保護

  • 通過構造函數來細化類型
  • instanceof的右側要求是一個構造函數,TypeScript將細化為:
    1. 此構造函數的 prototype屬性的類型,如果它的類型不為 any的話
    2. 構造簽名所返回的類型的聯合

可以為null的類型

  • 默認情況下,類型檢查器認為 nullundefined可以賦值給任何類型。 nullundefined是所有其它類型的一個有效值;--strictNullChecks標記可以解決此錯誤

  • 當使用了--strictNullChecks,可選參數和屬性會被自動地加上 | undefined

  • 可以通過js的寫法使用類型保護去除null

    function f(sn: string | null): string {
        if (sn == null) {
            return "default";
        }
        else {
            return sn;
        }
    }
    function f(sn: string | null): string {
        return sn || "default";
    }
    

    或者通過斷言的形式,寫法是添加!后綴

    function fixed(name: string | null): string {
      function postfix(epithet: string) {
        return name!.charAt(0) + '.  the ' + epithet; // ok
      }
      name = name || "Bob";
      return postfix("great");
    }
    

類型別名(type)

  • 通過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 Tree<T> = {
        value: T;
        left: Tree<T>;
        right: Tree<T>;
    }
    
  • 類型別名和接口的區別

    • 接口可以在任何地方使用,而類型別名不能出現在聲明右側的任何地方

      type Yikes = Array<Yikes>; // error
      
    • 接口創建了一個新名字,而類型別名不會

    • 類型別名不能被 extendsimplements(自己也不能 extendsimplements其它類型)

    • 當無法通過接口來描述一個類型,并且需要用到聯合類型或元組類型時會使用別名

字符串字面量類型

  • 字符串字面量可以實現類似枚舉的效果

    // 聯合類型    // 字符串字面量類型
    type Easing = "ease-in" | "ease-out" | "ease-in-out";
    class UIElement {
        animate(dx: number, dy: number, easing: Easing) {
            if (easing === "ease-in") { // 類型保護
                // ...
            }
            else if (easing === "ease-out") {
            }
            else if (easing === "ease-in-out") {
            }
            else {
                // error! should not pass null or undefined.
            }
        }
    }
    
  • 字符串字面量可用于區分函數重載

    function createElement(tagName: "img"): HTMLImageElement;
    function createElement(tagName: "input"): HTMLInputElement;
    // ... more overloads ...
    function createElement(tagName: string): Element {
        // ... code goes here ...
    }
    

數字字面量類型

  • 感覺沒啥特別用處

    function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 {
        // ...
    }
    

枚舉成員類型

  • 當每個枚舉成員都是用字面量初始化的時候枚舉成員是具有類型的,即枚舉成員類型

    enum ShapeKind {
        Circle,
        Square,
    }
    

可辨識聯合(Discriminated Unions)

  • 可辨識聯合的三個要素

    1. 具有普通的單例類型屬性— 可辨識的特征(即都有一個同樣的屬性用于分辨聯合的成員)
    2. 一個類型別名包含了那些類型的聯合— 聯合(即用類型別名來組織聯合)
    3. 此屬性上的類型保護
    interface Square {
        kind: "square"; // 要素一 可辨識特征
        size: number;
    }
    interface Rectangle {
        kind: "rectangle";
        width: number;
        height: number;
    }
    interface Circle {
        kind: "circle";
        radius: number;
    }
    // 要素二 用類型別名聯合
    type Shape = Square | Rectangle | Circle;
    
    function area(s: Shape) {
        switch (s.kind) { // 要素三 可辨識特征的類型保護
            case "square": return s.size * s.size;
            case "rectangle": return s.height * s.width;
            case "circle": return Math.PI * s.radius ** 2;
        }
    }
    

多態的 this類型

  • 通過返回this實現鏈式調用

    class BasicCalculator {
        public constructor(protected value: number = 0) { }
        public currentValue(): number {
            return this.value;
        }
        public add(operand: number): this {
            this.value += operand;
            return this;
        }
        public multiply(operand: number): this {
            this.value *= operand;
            return this;
        }
        // ... other operations go here ...
    }
    
    class ScientificCalculator extends BasicCalculator {
        public constructor(value = 0) {
            super(value);
        }
        public sin() {
            this.value = Math.sin(this.value);
            return this;
        }
        // ... other operations go here ...
    }
    
    let v = new ScientificCalculator(2)
            .multiply(5)
            .sin()
            .add(1)
            .currentValue();
    

索引類型(Index types)

function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
  return names.map(n => o[n]);
}

interface Person {
    name: string;
    age: number;
}
let person: Person = {
    name: 'Jarid',
    age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]

索引類型查詢操作符

  • keyof T
  • keyof T等同于 T上已知的公共屬性名的聯合,如 keyof Person完全可以與 'name' | 'age' 替換

索引訪問操作符

  • T[K] 類似于對象索引

映射類型

  • 通過轉化的方式從舊類型中創建新類型,如

    interface PersonPartial {
        name: string;
        age: number;
    }
    // 統一轉化為只讀
    type Readonly<T> = {
        readonly [P in keyof T]: T[P];
    }
    // 統一轉化為可選
    type Partial<T> = {
        [P in keyof T]?: T[P];
    }
    
  • typescript還內置了一些映射類型,可以直接使用,如

    type Pick<T, K extends keyof T> = {
        [P in K]: T[P];
    }
    type Record<K extends string, T> = {
        [P in K]: T;
    }
    // 預定義的有條件類型
    Exclude<T, U> -- 從T中剔除可以賦值給U的類型。
    Extract<T, U> -- 提取T中可以賦值給U的類型。
    NonNullable<T> -- 從T中剔除null和undefined。
    ReturnType<T> -- 獲取函數返回值類型。
    InstanceType<T> -- 獲取構造函數類型的實例類型
    
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容