Appearance
TypeScript 高级类型
TypeScript 的高级类型功能允许我们创建更复杂、更灵活的类型,从而提高代码的类型安全性。
交叉类型(Intersection Types)
交叉类型是将多个类型合并为一个类型,这让我们能够把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性。
typescript
interface ErrorHandling {
success: boolean;
error?: { message: string };
}
interface ArtworksData {
artworks: { title: string }[];
}
interface ArtistsData {
artists: { name: string }[];
}
// 这些接口被组合,以同时包含所需的全部功能
type ArtworksResponse = ArtworksData & ErrorHandling;
type ArtistsResponse = ArtistsData & ErrorHandling;
const handleArtistsResponse = (response: ArtistsResponse) => {
if (response.error) {
console.error(response.error.message);
return;
}
console.log(response.artists);
};
// 组合多个类型
interface Person {
name: string;
}
interface Contact {
phone: string;
}
interface Address {
address: string;
}
type PersonContact = Person & Contact & Address;
const person: PersonContact = {
name: "John Doe",
phone: "123-456-7890",
address: "123 Main St"
};
联合类型(Union Types)
联合类型表示一个值可以是几种类型之一,使用 | 分隔每个类型。
typescript
function formatId(id: number | string) {
// 联合类型需要类型守卫
if (typeof id === 'number') {
return id.toString();
} else {
return id;
}
}
// 联合类型在数组中的使用
let items: (string | number)[] = [1, 'hello', 2, 'world'];
// 联合类型与字面量类型
type Status = 'active' | 'inactive' | 'pending';
type Priority = 1 | 2 | 3 | 4 | 5;
function updateStatus(status: Status) {
console.log(`Status updated to: ${status}`);
}
类型守卫(Type Guards)
类型守卫是一种在运行时检查值的类型的方法,确保在特定代码块中类型的安全性。
typescript
// typeof 类型守卫
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 类型守卫
interface Padder {
getPaddingString(): string;
}
class SpaceRepeatingPadder implements Padder {
constructor(private numSpaces: number) {}
getPaddingString() {
return Array(this.numSpaces + 1).join(" ");
}
}
class StringPadder implements Padder {
constructor(private value: string) {}
getPaddingString() {
return this.value;
}
}
function getRandomPadder() {
return Math.random() < 0.5 ?
new SpaceRepeatingPadder(4) :
new StringPadder(" ");
}
let padder: Padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
padder.numSpaces; // 在这个分支中,TypeScript 知道这是 SpaceRepeatingPadder
}
// in 操作符类型守卫
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function move(animal: Fish | Bird) {
if ('swim' in animal) {
return animal.swim();
} else {
return animal.fly();
}
}
// 自定义类型守卫
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function getFood(pet: Fish | Bird) {
if (isFish(pet)) {
pet; // 这里 TypeScript 知道 pet 是 Fish
return "fish food";
} else {
pet; // 这里 TypeScript 知道 pet 是 Bird
return "bird food";
}
}
可辨识联合(Discriminated Unions)
可辨识联合是一种高级模式,它结合了联合类型和字面量类型。
typescript
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): number {
// 使用可辨识联合,TypeScript 可以推断出每种情况
switch (s.kind) {
case "square":
return s.size * s.size;
case "rectangle":
return s.width * s.height;
case "circle":
return Math.PI * s.radius ** 2;
}
}
// 确保处理了所有情况
function getArea(s: Shape): number {
switch (s.kind) {
case "square":
return s.size * s.size;
case "rectangle":
return s.width * s.height;
case "circle":
return Math.PI * s.radius ** 2;
default:
// 如果添加了新的形状类型,这里会报错
const _exhaustiveCheck: never = s;
return _exhaustiveCheck;
}
}
索引类型(Index Types)
索引类型允许我们表示对象中属性的类型依赖于另一个值。
typescript
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;
}
const person: Person = {
name: "Jarid",
age: 35
};
const values: string[] = pluck(person, ['name']); // string[]
const values2: (string | number)[] = pluck(person, ['name', 'age']); // (string | number)[]
// 索引访问操作符
interface Map<T> {
[key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number
映射类型(Mapped Types)
映射类型基于旧类型创建新类型,通过遍历键的联合来创建新类型。
typescript
// 基础映射类型
interface Person {
name: string;
age: number;
location: string;
}
type ReadonlyPerson = {
readonly [P in keyof Person]: Person[P];
};
type PartialPerson = {
[P in keyof Person]?: Person[P];
};
type PersonNullable = {
[P in keyof Person]: Person[P] | null;
};
// 映射类型工具
// 1. Partial<T> - 将所有属性设置为可选
type T0 = Partial<Person>;
// 2. Required<T> - 将所有属性设置为必需
type T1 = Required<Partial<Person>>;
// 3. Readonly<T> - 将所有属性设置为只读
type T2 = Readonly<Person>;
// 4. Pick<T, K> - 从 T 中选择 K 指定的属性
type T3 = Pick<Person, "name" | "age">;
// 5. Record<K, T> - 构造一个对象类型,其属性名的类型为 K,属性值的类型为 T
type T4 = Record<"x" | "y", Person>;
// 自定义映射类型
type Proxify<T> = {
[P in keyof T]: { value: T[P], validator: (value: T[P]) => boolean };
};
function setupProxies<T>(obj: T): { [P in keyof T]: { value: T[P], validator: (value: T[P]) => boolean } } {
const result = {} as any;
for (const key in obj) {
result[key] = {
value: obj[key],
validator: () => true
};
}
return result;
}
// 映射类型与条件类型的结合
type FunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T];
type FunctionProperties<T> = Pick<T, FunctionPropertyNames<T>>;
type NonFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends Function ? never : K
}[keyof T];
type NonFunctionProperties<T> = Pick<T, NonFunctionPropertyNames<T>>;
interface Part {
id: number;
name: string;
subparts: Part[];
updatePart(newName: string): void;
}
type T5 = FunctionPropertyNames<Part>; // "updatePart"
type T6 = NonFunctionPropertyNames<Part>; // "id" | "name" | "subparts"
条件类型(Conditional Types)
条件类型根据类型 T 是否可分配给类型 U 来选择不同的类型。
typescript
// 基础条件类型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
type T0 = TypeName<string>; // "string"
type T1 = TypeName<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
type T3 = TypeName<() => void>; // "function"
type T4 = TypeName<string[]>; // "object"
// 分配条件类型
type BoxedValue<T> = { value: T };
type BoxedArray<T> = { array: T[] };
type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;
type T10 = Boxed<string>; // BoxedValue<string>
type T11 = Boxed<number[]>; // BoxedArray<number>
type T12 = Boxed<string | number[]>; // BoxedValue<string> | BoxedArray<number>
// 条件类型约束
type Diff<T, U> = T extends U ? never : T;
type Filter<T, U> = T extends U ? T : never;
type T30 = Diff<"a" | "b" | "c", "a" | "e">; // "b" | "c"
type T31 = Filter<"a" | "b" | "c", "a" | "e">; // "a"
type T32 = Diff<string | number | (() => void), Function>; // string | number
type T33 = Filter<string | number | (() => void), Function>; // () => void
infer 关键字
infer 关键字用于在条件类型中推断类型。
typescript
// 提取函数返回值类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type T0 = GetReturnType<() => number>; // number
type T1 = GetReturnType<(x: string) => boolean>; // boolean
type T2 = GetReturnType<(a: number, b: string) => void>; // void
// 提取构造函数实例类型
type GetInstanceType<T> = T extends new (...args: any[]) => infer R ? R : any;
class Foo {
x = 0;
y = 0;
}
type T3 = GetInstanceType<typeof Foo>; // Foo
// 提取数组元素类型
type GetArrayElementType<T> = T extends (infer U)[] ? U : T;
type T4 = GetArrayElementType<string[]>; // string
type T5 = GetArrayElementType<number>; // number
// 提取 Promise 解包类型
type Unpromisify<T> = T extends Promise<infer U> ? U : T;
type T6 = Unpromisify<Promise<string>>; // string
type T7 = Unpromisify<number>; // number
// 复杂的 infer 使用
type ParamType<T> = T extends (param: infer P) => any ? P : T;
type T8 = ParamType<(name: string) => any>; // string
type T9 = ParamType<(id: number, name: string) => any>; // never (不匹配)
// 提取参数类型
type GetParameters<T> = T extends (...args: infer P) => any ? P : never;
type T10 = GetParameters<(a: number, b: string) => void>; // [number, string]
高级类型工具
深度只读类型
typescript
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]
};
interface Config {
apiUrl: string;
timeout: number;
nested: {
retries: number;
headers: {
'Content-Type': string;
};
};
}
type ReadonlyConfig = DeepReadonly<Config>;
深度部分类型
typescript
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
};
type PartialConfig = DeepPartial<Config>;
排除和提取类型
typescript
// Exclude<T, U> - 从 T 中排除 U
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
// Extract<T, U> - 从 T 中提取 U
type T1 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
// NonNullable<T> - 从 T 中排除 null 和 undefined
type T2 = NonNullable<string | number | undefined>; // string | number
实用高级类型模式
类型安全的事件系统
typescript
type EventMap = {
'user:login': { userId: number; timestamp: Date };
'user:logout': { userId: number; duration: number };
'app:error': { message: string; stack?: string };
};
type EventKey = keyof EventMap;
class TypedEventEmitter {
private listeners: { [K in EventKey]?: Array<(event: EventMap[K]) => void> } = {};
on<K extends EventKey>(event: K, listener: (event: EventMap[K]) => void) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
}
emit<K extends EventKey>(event: K, data: EventMap[K]) {
const eventListeners = this.listeners[event];
if (eventListeners) {
eventListeners.forEach(listener => listener(data));
}
}
}
const emitter = new TypedEventEmitter();
emitter.on('user:login', (data) => {
console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});
emitter.emit('user:login', {
userId: 123,
timestamp: new Date()
});
类型安全的 API 客户端
typescript
// 定义 API 端点和返回类型
type ApiEndpoints = {
'/users': { id: number; name: string; email: string }[];
'/users/:id': { id: number; name: string; email: string };
'/posts': { id: number; title: string; content: string }[];
'/posts/:id': { id: number; title: string; content: string };
};
type Endpoint = keyof ApiEndpoints;
class TypedApiClient {
async get<K extends Endpoint>(url: K): Promise<ApiEndpoints[K]> {
const response = await fetch(url);
return response.json();
}
}
const client = new TypedApiClient();
// TypeScript 知道返回类型
client.get('/users').then(users => {
// users 类型为 { id: number; name: string; email: string }[]
console.log(users[0].name); // 安全访问
});
小结
TypeScript 的高级类型功能提供了强大的类型操作能力,使我们能够创建更精确、更安全的类型定义。通过交叉类型、联合类型、类型守卫、映射类型和条件类型等特性,我们可以构建复杂但类型安全的代码结构。掌握这些高级类型概念对于编写高质量的 TypeScript 代码至关重要。