美文网首页我爱编程
学习笔记 - 高级类型

学习笔记 - 高级类型

作者: yibuyisheng | 来源:发表于2018-03-30 12:15 被阅读17次

    交叉类型

    包含所有类型的特性。比如 Person & Serializable & Loggable 交叉类型同时拥有了 PersonSerializableLoggable 这三种类型的成员。

    联合类型

    联合类型表示一个值可以是几种类型之一。number | string | boolean 表示一个值可以是 numberstringboolean

    如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。

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

    自定义类型保护

    类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。 要定义一个类型保护,我们只要简单地定义一个函数,它的返回值是一个类型谓词

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

    在这个例子里, pet is Fish 就是类型谓词。 谓词为 parameterName is Type 这种形式, parameterName 必须是来自于当前函数签名里的一个参数名。

    别名

    type Tree<T> = {
        value: T;
        left: Tree<T>;
        right: Tree<T>;
    };
    
    type Test = (name: Test) => void;
    
    // fail:
    // Type alias 'ArrayConfig' circularly references itself.
    type ArrayConfig = Array<number | ArrayConfig>;
    

    类型别名不能被 extendsimplements

    可辨识联合( Discriminated Unions )

    interface Square {
        kind: "square";
        size: number;
    }
    interface Rectangle {
        kind: "rectangle";
        width: number;
        height: number;
    }
    interface Circle {
        kind: "circle";
        radius: number;
    }
    

    首先我们声明了将要联合的接口。每个接口都有 kind 属性但有不同的字符串字面量类型。 kind 属性称做可辨识的特征标签,其它的属性则特定于各个接口。注意,目前各个接口间是没有联系的。下面我们把它们联合到一起:

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

    完整性检查

    当没有涵盖所有可辨识联合的变化时,我们想让编译器可以通知我们。比如,如果我们添加了 TriangleShape ,我们同时还需要更新 area :

    type Shape = Square | Rectangle | Circle | Triangle;
    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;
        }
        // should error here - we didn't handle case "triangle"
    }
    

    有两种方式可以实现。 首先是启用 --strictNullChecks 并且指定一个返回值类型:

    function area(s: Shape): number { // error: returns number | undefined
        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;
        }
    }
    

    因为 switch 没有包涵所有情况,所以 TypeScript 认为这个函数有时候会返回 undefined 。如果你明确地指定了返回值类型为 number ,那么你会看到一个错误,因为实际上返回值的类型为 number | undefined

    第二种方法使用 never 类型,编译器用它来进行完整性检查:

    function assertNever(x: never): never {
        throw new Error("Unexpected object: " + x);
    }
    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;
            default: return assertNever(s); // error here if there are missing cases
        }
    }
    

    这里, assertNever 检查 s 是否为 never 类型 — 即为除去所有可能情况后剩下的类型。如果你忘记了某个 case ,那么 s 将具有一个真实的类型并且你会得到一个错误。这种方式需要你定义一个额外的函数,但是在你忘记某个 case 的时候也更加明显。

    相关文章

      网友评论

        本文标题:学习笔记 - 高级类型

        本文链接:https://www.haomeiwen.com/subject/lznbcftx.html