交叉类型(Intersection Types)
-
某个类型的对象同时拥有多种类型的成员,交叉类型使用
&
创建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();
联合类型(Union Types)
与交叉类型对应,联合类型表示一个值可以是几种类型之一,用
|
表示-
如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员
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(); }
可以通过定义类型保护避免反复断言
function isFish(pet: Fish | Bird): pet is Fish { return (<Fish>pet).swim !== undefined; } // 返回值pet is Fish 是一个类型谓词 // pet为函数参数,Fish是联合类型的成员之一 // 调用一次类型保护后,TypeScript会将变量缩减为那个具体的类型 if (isFish(pet)) { pet.swim(); } else { pet.fly(); }
typeof
类型保护(基础类型保护)
- 只有两种形式
typeof v === "typename"
和typeof v !== "typename"
-
"typename"
必须是"number"
,"string"
,"boolean"
或"symbol"
,与其它字符串比较不会被识别为类型保护
instanceof
类型保护
- 通过构造函数来细化类型
-
instanceof
的右侧要求是一个构造函数,TypeScript将细化为:- 此构造函数的
prototype
属性的类型,如果它的类型不为any
的话 - 构造签名所返回的类型的联合
- 此构造函数的
可以为null的类型
默认情况下,类型检查器认为
null
与undefined
可以赋值给任何类型。null
与undefined
是所有其它类型的一个有效值;--strictNullChecks
标记可以解决此错误当使用了
--strictNullChecks
,可选参数和属性会被自动地加上| undefined
-
可以通过js的写法使用类型保护去除null
function f(sn: string | null): string { if (sn == null) { return "default"; } else { return sn; } } function f(sn: string | null): string { return sn || "default"; }
或者通过断言的形式,写法是添加
!
后缀function fixed(name: string | null): string { function postfix(epithet: string) { return name!.charAt(0) + '. the ' + epithet; // ok } name = name || "Bob"; return postfix("great"); }
类型别名(type)
通过
type
关键字取别名-
别名不会新建一个类型,只是已有类型的引用
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 Yikes = Array<Yikes>; // error
接口创建了一个新名字,而类型别名不会
类型别名不能被
extends
和implements
(自己也不能extends
和implements
其它类型)当无法通过接口来描述一个类型,并且需要用到联合类型或元组类型时会使用别名
-
字符串字面量类型
-
字符串字面量可以实现类似枚举的效果
// 联合类型 // 字符串字面量类型 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. } } }
-
字符串字面量可用于区分函数重载
function createElement(tagName: "img"): HTMLImageElement; function createElement(tagName: "input"): HTMLInputElement; // ... more overloads ... function createElement(tagName: string): Element { // ... code goes here ... }
数字字面量类型
-
感觉没啥特别用处
function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 { // ... }
枚举成员类型
-
当每个枚举成员都是用字面量初始化的时候枚举成员是具有类型的,即枚举成员类型
enum ShapeKind { Circle, Square, }
可辨识联合(Discriminated Unions)
-
可辨识联合的三个要素
- 具有普通的单例类型属性— 可辨识的特征(即都有一个同样的属性用于分辨联合的成员)
- 一个类型别名包含了那些类型的联合— 联合(即用类型别名来组织联合)
- 此属性上的类型保护
interface Square { kind: "square"; // 要素一 可辨识特征 size: number; } interface Rectangle { kind: "rectangle"; width: number; height: number; } interface Circle { kind: "circle"; radius: number; } // 要素二 用类型别名联合 type Shape = Square | Rectangle | Circle; function area(s: Shape) { switch (s.kind) { // 要素三 可辨识特征的类型保护 case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case "circle": return Math.PI * s.radius ** 2; } }
多态的 this
类型
-
通过返回this实现链式调用
class BasicCalculator { public constructor(protected value: number = 0) { } public currentValue(): number { return this.value; } public add(operand: number): this { this.value += operand; return this; } public multiply(operand: number): this { this.value *= operand; return this; } // ... other operations go here ... } class ScientificCalculator extends BasicCalculator { public constructor(value = 0) { super(value); } public sin() { this.value = Math.sin(this.value); return this; } // ... other operations go here ... } let v = new ScientificCalculator(2) .multiply(5) .sin() .add(1) .currentValue();
索引类型(Index types)
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
索引类型查询操作符
keyof T
-
keyof T
等同于T
上已知的公共属性名的联合,如keyof Person
完全可以与'name' | 'age'
替换
索引访问操作符
-
T[K]
类似于对象索引
映射类型
-
通过转化的方式从旧类型中创建新类型,如
interface PersonPartial { name: string; age: number; } // 统一转化为只读 type Readonly<T> = { readonly [P in keyof T]: T[P]; } // 统一转化为可选 type Partial<T> = { [P in keyof T]?: T[P]; }
-
typescript还内置了一些映射类型,可以直接使用,如
type Pick<T, K extends keyof T> = { [P in K]: T[P]; } type Record<K extends string, T> = { [P in K]: T; } // 预定义的有条件类型 Exclude<T, U> -- 从T中剔除可以赋值给U的类型。 Extract<T, U> -- 提取T中可以赋值给U的类型。 NonNullable<T> -- 从T中剔除null和undefined。 ReturnType<T> -- 获取函数返回值类型。 InstanceType<T> -- 获取构造函数类型的实例类型