抄录于掘金小册 作者:寻找海蓝 《深入浅出TypeScript:从基础知识到类型编程》

# 高级类型之交叉类型、联合类型、类型别名

# 交叉类型

交叉类型是将多个类型合并成一个类型,这让我们可以将现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。

在JavaScript中,混入是一种非常常见的模式,在这种模式中,你可以从两个对象中创建一个新对象,新对象会拥有着两个对象所有的功能。

交叉类型可以让你安全的使用此种模式:

interface IAnyObject {
    [prop:string]:any
}
function mixin<T extends IAnyObject,U extends IAnyObject>(first:T,second:U):T & U {
    const result = <T & U> {};
    for(let id in first) {
        (<T>result)[id] = first[id]
    }
    for(let id in second) {
        if(!result.hasOwnProperty(id)) {
            (<U>result)[id] = second[id];
        }
    }
    return result  
}
const x = mixin({a: "hello"},{b: 42});
const a = x.a;
const b = x.b;

# 联合类型

在JavaScript中,你希望属性为多种类型之一,如字符串或数组。

z这就是联合类型所能派上用场的地方(它使用|作为标记,如string|number)

function formatCommandline(command:string[]|string) {
    let line = '';
    if(typeof command === 'string') {
        line = command.trim();
    }else {
        line = command.join(' ').trim();
    }
}

联合类型表示一个值可以是几种类型之一,我们用竖线(|)分隔每个类型,所以number|string|boolean表示一个值可以是number、string或boolean。

# 类型别名

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

你可以使用type SomeName = someValidTypeAnnotation的语法创建类型别名:

type some = boolean|string;
const b:some = true;
const c:some = 'hello';

此外类型别名可以是泛型:

type Container<T> = { value: T };

也可以使用类型别名来在属性里引用自己:

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

类型别名看起来跟interface非常像,那么应该如何区分两者。

interface只能用于定义对象类型,而type的声明方式除了对象之外还可以定义交叉、联合、原始类型等,类型声明的方式适用范围显然更加广泛。

但是interface也有其特定的用处:

  • interface方式可以实现接口的extends和implement
  • interface可以实现接口合并声明
type Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias):Alias;
declare function interfaced(arg: Interface): Interface

此外,接口创建了一个新的名字,可以在其他任何地方使用,类型别名并不创建新名字,比如,错误信息就不会使用别名。