高级类型
大约 3 分钟
高级类型
交叉类型 (Intersection Types)
交叉类型是将多个类型合并为一个类型。 这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。
interface Person {
name: string;
}
interface Loggable {
log(): void;
}
type PersonLoggable = Person & Loggable;
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;
}
联合类型 (Union Types)
联合类型表示一个值可以是几种类型之一。 我们用竖线 | 分隔每个类型,所以 number | string | boolean 表示一个值可以是 number,string,或 boolean。
function padLeft(value: string, padding: string | number) {
// ...
}
let indentedString = padLeft("Hello world", true); // errors during compilation
类型保护与区分类型 (Type Guards)
用户自定义的类型保护
interface Fish { swim(): void; }
interface Bird { fly(): void; }
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
// 调用
// if (isFish(pet)) {
// pet.swim();
// }
// else {
// pet.fly();
// }
typeof 类型保护
typeof 类型保护只有两种形式能被识别:typeof v === "typename" 和 typeof v !== "typename","typename" 必须是 "number","string","boolean" 或 "symbol"。
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
instanceof 类型保护
instanceof 类型保护是通过构造函数来细化类型的一种方式。
class Bird {
fly() { console.log("bird fly"); }
layEggs() { console.log("bird lay eggs"); }
}
class Fish {
swim() { console.log("fish swim"); }
layEggs() { console.log("fish lay eggs"); }
}
function getRandomPet() {
return Math.random() > 0.5 ? new Bird() : new Fish();
}
let pet = getRandomPet();
if (pet instanceof Bird) {
pet.fly();
}
if (pet instanceof Fish) {
pet.swim();
}
类型别名 (Type Aliases)
类型别名会给一个类型起个新名字。 类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
}
else {
return n();
}
}
接口 vs 类型别名
- 接口创建了一个新的名字,可以在其它任何地方使用。 类型别名并不创建新名字—比如,错误信息就不会使用别名。
- 类型别名不能被
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.
}
}
}
let button = new UIElement();
button.animate(0, 0, "ease-in");
// button.animate(0, 0, "uneasy"); // error
索引类型 (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: 索引类型查询操作符。 对于任何类型T,keyof T的结果为T上已知的公共属性名的联合。T[K]: 索引访问操作符。
映射类型 (Mapped Types)
TypeScript 提供了从旧类型中创建新类型的一种方式 — 映射类型。 在映射类型里,新类型以相同的形式去转换旧类型里每个属性。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type Partial<T> = {
[P in keyof T]?: T[P];
}
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
由映射类型进行推断
type Proxy<T> = {
get(): T;
set(value: T): void;
}
type Proxify<T> = {
[P in keyof T]: Proxy<T[P]>;
}
function proxify<T>(o: T): Proxify<T> {
// ... wrap proxies ...
return o as any;
}
let props = { rooms: 4 };
let proxyProps = proxify(props);
let originalProps = unproxify(proxyProps);
function unproxify<T>(t: Proxify<T>): T {
let result = {} as T;
for (const k in t) {
result[k] = t[k].get();
}
return result;
}
Loading...
