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

学习笔记 - 高级类型

作者: 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