Typescipt 高级类型

415 阅读3分钟

参考文章
www.tslang.cn/docs/handbo…
TypeScript 真香系列——接口篇

交叉类型

交叉类型是将多个类型合并成一个类型, 用&分割每个类型。
我们大多是在混入mixins或其他不适合典型面向对象模型的地方看到交叉对象使用。

T & U

下面是如何创建混入的一个简单的例子


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();

联合类型

联合类型表示一个值可以是几种类型,用竖线(|)分割每个类型

number | string

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

类型保护

类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里面的类型。要定义一个类型保护,我们只要简单的定义一个函数,它的返回值是一个类型谓词。谓词的形式为parameterName is TypeparamterName必须是来自于当前函数签名里的一个参数名。

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

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

instanceof 类型保护 是通过构造函数来细化类型的一种方式。instanceof的右侧要求是一个构造函数,TypeScript将细化为:

  • 此构造函数的 prototype属性的类型,如果它的类型不为 any的话
  • 构造签名所返回的类型的联合

TypeScript具有两种特殊的类型, nullundefined,它们分别具有值nullundefined. 默认情况下,类型检查器认为 nullundefined可以赋值给任何类型。 nullundefined是所有其它类型的一个有效值。

使用--strictNullChecks标记,当你声明一个变量时,它不会自动地包含 nullundefined;可选参数会被自动地加上 | undefined

类型别名

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

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 使用场景
type StringOrNumber = string | number;  
type Text = string | { text: string };  
type NameLookup = Dictionary<string, Person>;  
type Callback<T> = (data: T) => void;  
type Pair<T> = [T, T];  
type Coordinates = Pair<number>;  
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };

// type 语句中还可以使用 typeof 获取实例的 类型进行赋值
let div = document.createElement('div');
type B = typeof div

类型别名和接口的区别:

  • 接口创建了一个新的名字,可以在其它任何地方使用。 类型别名并不创建新名字
  • 类型别名不能被 extendsimplements(自己也不能 extendsimplements其它类型)
  • 接口可以声明合并,type不行
interface User {
  name: string
  age: number
}

interface User {
  sex: string
}

/*
User 接口为 {
  name: string
  age: number
  sex: string 
}
*/

字符串字面量类型

字符串字面量类型允许你指定字符串必须的固定值。 在实际应用中,字符串字面量类型可以与联合类型,类型保护和类型别名很好的配合。 通过结合使用这些特性,你可以实现类似枚举类型的字符串,还可以用于区分函数重载


type Easing = "ease-in" | "ease-out" | "ease-in-out";

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