05-ts-高级类型

这是我参与更文挑战的第6天,活动详情查看: 更文挑战web


高级类型

交叉类型

交叉类型是将多个类型合并为一个类型。 咱们能够把现有的多种类型叠加到一块儿成为一种类型,它包含了所需的全部类型的特性。 例如, Person & Serializable & Loggable同时是 Person 和 Serializable 和 Loggable。 就是说这个类型的对象同时拥有了这三种类型的成员。json

咱们大可能是在混入(mixins)或其它不适合典型面向对象模型的地方看到交叉类型的使用。 (在JavaScript里发生这种状况的场合不少!) 下面是如何建立混入的一个简单例子:markdown

function extend<T, U>(first: T, second: U): T & 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();

复制代码

联合类型

联合类型与交叉类型颇有关联,可是使用上却彻底不一样。 偶尔你会遇到这种状况,一个代码库但愿传入 number或 string类型的参数。 例以下面的函数:ide

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); // returns " Hello world"
复制代码

这里的padding类型是any, 实际咱们须要的是number或者string, 若是传入的类型不是number或者string, ts也不会报错。咱们就能够使用联合类型, number | string函数

若是一个值是联合类型,咱们只能访问此联合类型的全部类型里共有的成员。post

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

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

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

let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim();    // errors
复制代码

pet的类型是 Fish | Bird, layEggs是共有的方法,访问是ok的, 而swim是Fish独有的, 若是类型是Bird, 就没有swim方法了,就会报错。ui

类型保护与区分类型

联合类型适合于那些值能够为不一样类型的状况。 但当咱们想确切地了解是否为 Fish时怎么办? JavaScript里经常使用来区分2个可能值的方法是检查成员是否存在。 如以前说起的,咱们只能访问联合类型中共同拥有的成员。url

为了让这段代码工做,咱们要使用类型断言:spa

let pet = getSmallPet();

if ((<Fish>pet).swim) {
    (<Fish>pet).swim();
}
else {
    (<Bird>pet).fly();
}
复制代码

用户自定义的类型保护

TypeScript里的 类型保护机制让它成为了现实。 类型保护就是一些表达式,它们会在运行时检查以确保在某个做用域里的类型。 要定义一个类型保护,咱们只要简单地定义一个函数,它的返回值是一个 类型谓词:3d

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

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

每当使用一些变量调用 isFish时,TypeScript会将变量缩减为那个具体的类型,只要这个类型与变量的原始类型是兼容的。

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}'.`);
}
复制代码

这些typeof类型保护只有两种形式能被识别: typeof v === "typename"和 typeof v !== "typename", "typename"必须是 "number", "string", "boolean"或 "symbol"。 可是TypeScript并不会阻止你与其它字符串比较,语言不会把那些表达式识别为类型保护。

能够为null的类型

TypeScript具备两种特殊的类型, null和 undefined,它们分别具备值null和undefined. 默认状况下,类型检查器认为 null与 undefined能够赋值给任何类型。 null与 undefined是全部其它类型的一个有效值。 这也意味着,你阻止不了将它们赋值给其它类型,就算是你想要阻止这种状况也不行。

let s = "foo";
s = null; // 错误, 'null'不能赋值给'string'
let sn: string | null = "bar";
sn = null; // 能够

sn = undefined; // error, 'undefined'不能赋值给'string | null'

复制代码

可选参数和可选属性

tsconfig.json使用了 --strictNullChecks,可选参数会被自动地加上 | undefined:

function f(x: number, y?: number) {
    return x + (y || 0);
}
f(1, 2);
f(1);
f(1, undefined);
f(1, null); // error, 'null' is not assignable to 'number | undefined'
复制代码

类型保护和类型断言

因为能够为null的类型是经过联合类型实现,那么你须要使用类型保护来去除 null。 幸运地是这与在JavaScript里写的代码一致:

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

这里很明显地去除了 null,你也能够使用短路运算符:

function f(sn: string | null): string {
    return sn || "default";
}
复制代码

若是编译器不可以去除 null或 undefined,你能够使用类型断言手动去除。 语法是添加 !后缀: identifier!从 identifier的类型里去除了 null和 undefined:

let obj = {}

obj!.name
复制代码

类型别名

类型别名会给一个类型起个新名字。 类型别名有时和接口很像,可是能够做用于原始值,联合类型,元组以及其它任何你须要手写的类型。

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 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"); 
复制代码

Record

ts中,若是要定义一个对象的key,value类型,该怎么作呢?这时候就须要用到 TS 的 Record 了。

interface Person { name: string; }

type Key = "p1" | "p2" | "p3";

const person: Record<Key, Person> = { p1: { name: "zs" }, p2: { name: "ls" }, p3: { name: "ww" }, }; Record 后面的泛型就是对象 键和值的类型。

ReturnType

ReturnType<T> -- 获取函数返回值类型.

type T10 = ReturnType<() => string>;  // string
type T11 = ReturnType<(s: string) => void>;  // void
type T12 = ReturnType<(<T>() => T)>;  // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]
type T15 = ReturnType<any>;  // any
type T16 = ReturnType<never>;  // any
type T17 = ReturnType<string>;  // Error
type T18 = ReturnType<Function>;  // Error

复制代码
相关文章
相关标签/搜索