交叉类型
交叉类型是将多个类型合并成一个类型, 用&
分割每个类型。
我们大多是在混入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 Type
,paramterName
必须是来自于当前函数签名里的一个参数名。
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具有两种特殊的类型, null
和 undefined
,它们分别具有值null
和undefined
. 默认情况下,类型检查器认为 null
与 undefined
可以赋值给任何类型。 null
与 undefined
是所有其它类型的一个有效值。
使用--strictNullChecks
标记,当你声明一个变量时,它不会自动地包含 null
或 undefined
;可选参数会被自动地加上 | 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
类型别名和接口的区别:
- 接口创建了一个新的名字,可以在其它任何地方使用。 类型别名并不创建新名字
- 类型别名不能被
extends
和implements
(自己也不能extends
和implements
其它类型) - 接口可以声明合并,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 ...
}