TypeScript入門-高級類型

高級類型

 

交叉類型

交叉類型,就是將多個類型合併爲一個新的類型,這個新的類型具備這多個類型的成員,含有這幾個類型的全部特性,是他們的綜合體,像是集合的並集dom

例子:函數

function extend<T,U>(first: T, second: U): T&U {
    let result = <T & U>{};
    for (let id in first) {
        (<any>result)[id] = first[id];
    }
    for (let id in second) {
        if (!result.hasOwnProperty(id)) {
            (<any>result)[id] = second[id];
        }
    }
    return result;
}

class Person {
    constructor(public name: string) {
    }
}

interface Loggable {
    log(): void;
}

class myLoggable implements Loggable {
    log() {
        console.log('qwe');
    }
}

let jim = extend(new Person('qq'), new myLoggable());
console.log(jim.name);
jim.log();

例子中jim有Person中的name屬性也有myLoggable中的log()方法this

 

聯合類型

聯合類型,不像是交叉類型是多個類型的合集,表示是這多個類型中的一種類型,像是集合中的交集,只有多個類型中共有的特性才能夠被調用url

例如要向一個函數傳遞參數,這個參數多是number也多是stringspa

function padLeft(value: string, padding: any) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

padLeft("Hello world", 4); // "    Hello world"

這裏存在一個問題,將padding定義爲any,表示咱們能夠傳遞任何值給padding,這個錯誤TypeScript是不會報錯的,只有在編譯時纔會報錯code

 

解決這個問題,能夠採用聯合類型,用豎線分割每一個類型,表示是這幾個類型中的一種對象

function padLeft(value: string, padding: string | number) {
    ........
}
let f = padLeft("Hello world", true); // error

 

若是一個值是聯合類型,就只能訪問這多個類型所共有的成員blog

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function getSmallPet(): Fish | Bird {
    ...
}

let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim();    // errors

getSmallPet的返回類型就是一個聯合類型,因此pet就只能訪問Bird和Fish的共有成員layEggs()接口

在上面的例子中,咱們不知道pet究竟是那種類型,因此就不可能去訪問哪些不是公共的成員,若是咱們知道了pet的類型,就能夠訪問該類型的全部成員了ip

 

類型保護和區分類型

爲了解決上面提到的具體類型的肯定問題,須要引入類型斷言(類型轉換)

let pet = getSmallPet();

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

問題顯而易見,每次都須要對pet進行類型轉換,麻煩

 

用戶自定義的類型保護

類型保護就能夠解決上述每次都要進行類型斷言的缺點,類型保護就是一些表達式,它們會在運行時檢查以確保在某個做用域裏的類型。 要定義一個類型保護,咱們只要簡單地定義一個函數,它的返回值是一個類型斷言

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined;
}

 

pet is Fish就是類型斷言。 一個斷言是 parameterName is Type這種形式,parameterName必須是來自於當前函數簽名裏的一個參數名。

// 'swim' 和 'fly' 調用都沒有問題了

if (isFish(pet)) {
    pet.swim();
}
else {
    pet.fly();
}

TypeScript不只知道在if裏是Fish,並且還知道在else裏是Bird類型

 

typeof類型保護

咱們將以前的padLeft代碼改用類型斷言來實現

function isNumber(x: any): x is number {
    return typeof x === "number";
}

function isString(x: any): x is string {
    return typeof x === "string";
}

function padLeft(value: string, padding: string | number) {
    if (isNumber(padding)) {
        return Array(padding + 1).join(" ") + value;
    }
    if (isString(padding)) {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

 

若是要按這樣寫就要爲每一個原始類型寫一個函數,麻煩。TypeScript會把"typeof v === typeofname"和"typeof v !== typeofname"看作是類型保護,因此就沒必要爲一個原始類型寫一個函數,直接用typeof就能夠了

function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

 

instanceof類型保護

instanceof類型保護是經過構造函數來細化類型的一種方式

interface Padder {
    getPaddingString(): string
}

class SpaceRepeatingPadder implements Padder {
    constructor(private numSpaces: number) { }
    getPaddingString() {
        return Array(this.numSpaces + 1).join(" ");
    }
}

class StringPadder implements Padder {
    constructor(private value: string) { }
    getPaddingString() {
        return this.value;
    }
}

function getRandomPadder() {
    return Math.random() < 0.5 ?
        new SpaceRepeatingPadder(4) :
        new StringPadder("  ");
}

// 類型爲SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();

if (padder instanceof SpaceRepeatingPadder) {
    padder; // 類型細化爲'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
    padder; // 類型細化爲'StringPadder'
}

 

類型別名

類型別名就是給類型起一個別名,並且能夠用於基礎的數據類型

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 Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;

在上面的代碼中interfaced返回值的類型是Interface,而aliased返回值的類型是對象字面量

類型別名與接口還有一個地方不一樣的是類型別名不能夠被extends和implements

上面說了類型別名與接口的兩個不一樣點,固然也有相同點,即均可以使用泛型

type Tree<T> = {
    value: T;
    left: Tree<T>;
    right: Tree<T>;
}

 

字符串字面量類型

字符串字面量類型能夠與聯合類型,類型保護和類型別名很好的配合

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.
        }
    }
}

let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here

只能從規定的三種類型中選擇一種類型進行傳遞,其餘的類型會報錯

 

可辨識聯合

能夠合併字符串字面量類型,聯合類型,類型保護和類型別名來建立一個叫作可辨識聯合的高級模式

先定義三個將要聯合的接口,每一個接口都有kind屬性,可是值不一樣,king屬性能夠做爲可辨識的特徵和標識

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;
    }
}

 

注意:若是在Shape中添加新的類型,那麼在switch下也要添加相應的判斷

 

參考資料:

TypeScript中文網 · TypeScript——JavaScript的超集

相關文章
相關標籤/搜索