本节将对TypeScript中类型的高级特性做详细讲解,包括交叉类型、类型别名、类型保护等。
交叉类型(Intersection Type)是将多个类型通过“&”符号合并成一个新类型,新类型将包含所有类型的特性。例如有Person和Programmer两个类(如下代码所示),当man变量的类型声明为Person&Programmer时,它就能使用两个类的成员:name属性和work()方法。
class Person {name: string;
}
class Programmer {work() { }
}
let man: Person&Programmer;
man.name;
man.work();
交叉类型常用于混入(mixin)或其它不适合典型面向对象模型的场景,例如在下面的示例中,通过交叉类型让新对象obj同时包含a和b两个属性。
function extend(first: T, second: U): T & U {const result = {};for (let prop in first) {(result)[prop] = first[prop];}for (let prop in second) {if (!result.hasOwnProperty(prop)) {(result)[prop] = second[prop];}}return result;
}
let obj = extend({ a: 1 }, { b: 2 });
TypeScript提供了type关键字,用于创建类型别名,可作用于基本类型、联合类型、交叉类型和泛型等任意类型,如下所示。
type Name = string;//基本类型
type Func = () => string;//函数
type Union = Name | Func;//联合类型
type Tuple = [number, number]; //元组
type Generic = { value: T };//泛型
注意,起别名不是新建一个类型,而是提供一个可读性更高的名称。类型别名可在属性里引用自身,但不能出现在声明的右侧,如下所示。
type Tree = {value: T;left: Tree;right: Tree;
}
type Arrs = Array;//错误
当使用联合类型时,只能访问它们的公共成员。假设有一个func()函数,它的参数是由Person和Programmer两个类组成的联合类型,如下代码所示。
function func(man: Person | Programmer) {if((man).run) {(man).run();}else {(man).work();}
}
虽然利用类型断言可以确定参数类型,在编译阶段避免了报错,但是多次调用类型断言未免过于繁琐。于是TypeScript就引入了类型保护机制,替代类型断言。类型保护(Type Guard)是一些表达式,允许在运行时检查类型,缩小类型范围。
TypeScript可将typeof运算符识别成类型保护,从而就能直接在代码里检查类型(如下所示),其计算结果是个字符串,包括“number”、“string”、“boolean”或“symbol”等关键字。
function send(data: number | string) {if (typeof data === "number") {//...} else if(typeof data === "string") {//...}
}
TypeScript也可将instanceof运算符识别成类型保护,通过构造函数来细化类型,检测实例和类是否有关联,如下所示。
function work(man: Person | Programmer) {if (man instanceof Person) {//...} else if(man instanceof Programmer) {//...}
}