TypeScript高级类型技巧
2023-07-10作者: YZ12 分钟
TypeScriptJavaScript前端开发编程语言
# TypeScript高级类型技巧
TypeScript的类型系统非常强大,远超过简单的类型注解。掌握高级类型技巧可以帮助你编写更安全、更可维护的代码。本文将介绍一些TypeScript中的高级类型技术,帮助你充分利用TypeScript的类型系统。
## 条件类型
条件类型允许你根据类型关系创建新的类型,类似于JavaScript中的条件表达式。
### 基本语法
```typescript
type IsString = T extends string ? true : false;
// 使用示例
type A = IsString; // true
type B = IsString; // false
```
### 实际应用
条件类型在处理联合类型和泛型约束时特别有用:
```typescript
// 从联合类型中提取特定类型
type Extract = T extends U ? T : never;
type NumberOrString = number | string | boolean;
type JustNumberOrString = Extract;
// 结果: number | string
// 排除特定类型
type Exclude = T extends U ? never : T;
type JustBoolean = Exclude;
// 结果: boolean
```
## 映射类型
映射类型允许你基于旧类型创建新类型,通过遍历现有类型的属性。
### 基本语法
```typescript
type Readonly = {
readonly [P in keyof T]: T[P];
};
// 使用示例
interface Person {
name: string;
age: number;
}
type ReadonlyPerson = Readonly;
// 等同于: { readonly name: string; readonly age: number; }
```
### 实际应用
映射类型可以用于创建工具类型,如Partial、Required等:
```typescript
// 使所有属性可选
type Partial = {
[P in keyof T]?: T[P];
};
// 使所有属性必需
type Required = {
[P in keyof T]-?: T[P];
};
// 选取特定属性
type Pick = {
[P in K]: T[P];
};
// 使用示例
interface User {
id: number;
name: string;
email: string;
address?: string;
}
type UserBasicInfo = Pick;
// 结果: { id: number; name: string; }
```
## 模板字面量类型
TypeScript 4.1引入了模板字面量类型,允许你基于字符串字面量类型创建新的字符串字面量类型。
### 基本语法
```typescript
type World = 'world';
type Greeting = `Hello ${World}`;
// 结果: "Hello world"
```
### 实际应用
模板字面量类型在创建基于现有类型的字符串类型时非常有用:
```typescript
// 创建CSS属性类型
type CSSProperty = 'color' | 'background-color' | 'margin';
type CSSValue = string | number;
type CSSPropertyMap = {
[K in CSSProperty]: CSSValue;
};
// 创建事件处理器类型
type EventName = 'click' | 'change' | 'mouseenter';
type EventHandlerName = `on${Capitalize}`;
// 结果: "onClick" | "onChange" | "onMouseenter"
// 使用内置的字符串操作类型
type Greeting = "hello world";
type ShoutyGreeting = Uppercase;
// 结果: "HELLO WORLD"
type QuietGreeting = Lowercase;
// 结果: "hello world"
type CapitalizedGreeting = Capitalize;
// 结果: "Hello world"
type UncapitalizedGreeting = Uncapitalize;
// 结果: "hello world"
```
## 递归类型
TypeScript支持递归类型定义,这在处理嵌套结构时非常有用。
### 基本示例
```typescript
// 定义嵌套数组类型
type NestedArray = Array>;
const nested: NestedArray = [1, 2, [3, 4, [5, 6]]];
// 定义嵌套对象类型
type NestedObject = {
value: T;
children?: NestedObject[];
};
const tree: NestedObject = {
value: 'root',
children: [
{ value: 'child1' },
{
value: 'child2',
children: [{ value: 'grandchild' }]
}
]
};
```
### JSON类型示例
```typescript
type JSONPrimitive = string | number | boolean | null;
type JSONArray = JSONValue[];
type JSONObject = { [key: string]: JSONValue };
type JSONValue = JSONPrimitive | JSONArray | JSONObject;
const json: JSONValue = {
name: 'John',
age: 30,
isActive: true,
address: {
street: '123 Main St',
city: 'Anytown'
},
hobbies: ['reading', 'coding', { type: 'sports', favorite: 'basketball' }]
};
```
## 类型推断与infer关键字
`infer`关键字允许你在条件类型中推断和捕获类型。
### 基本语法
```typescript
type ReturnType = T extends (...args: any[]) => infer R ? R : any;
// 使用示例
function createUser(name: string, age: number) {
return { name, age };
}
type User = ReturnType;
// 结果: { name: string; age: number; }
```
### 实际应用
`infer`在提取复杂类型的组件时非常有用:
```typescript
// 提取Promise的值类型
type UnwrapPromise = T extends Promise ? U : T;
type Result = UnwrapPromise>;
// 结果: string
// 提取函数参数类型
type Parameters = T extends (...args: infer P) => any ? P : never;
type Params = Parameters<(a: number, b: string) => void>;
// 结果: [number, string]
// 提取构造函数的实例类型
type InstanceType = T extends new (...args: any[]) => infer R ? R : any;
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}
type PersonInstance = InstanceType;
// 结果: Person
```
## 联合类型的分布式条件类型
当在泛型中使用条件类型时,联合类型会被分布处理。
```typescript
type ToArray = T extends any ? T[] : never;
type NumberOrStringArray = ToArray;
// 结果: number[] | string[]
// 而不是: (number | string)[]
// 如果要避免分布式行为
type ToArrayNonDist = [T] extends [any] ? T[] : never;
type NumberOrStringArrayNonDist = ToArrayNonDist;
// 结果: (number | string)[]
```
## 类型体操示例
以下是一些展示TypeScript类型系统强大功能的高级示例:
### 深度Readonly
```typescript
type DeepReadonly = {
readonly [P in keyof T]: T[P] extends object
? T[P] extends Function
? T[P]
: DeepReadonly
: T[P];
};
interface User {
id: number;
name: string;
settings: {
theme: string;
notifications: {
email: boolean;
sms: boolean;
};
};
}
type ReadonlyUser = DeepReadonly;
// 所有嵌套属性都是只读的
```
### 类型安全的事件发射器
```typescript
type EventMap = {
click: { x: number; y: number };
change: { value: string };
submit: { data: Record };
};
class TypedEventEmitter> {
private listeners: {
[E in keyof Events]?: Array<(data: Events[E]) => void>;
} = {};
on(event: E, listener: (data: Events[E]) => void) {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
return this;
}
emit(event: E, data: Events[E]) {
if (!this.listeners[event]) return false;
this.listeners[event]!.forEach(listener => listener(data));
return true;
}
}
const emitter = new TypedEventEmitter();
// 类型安全的事件监听
emitter.on('click', data => {
console.log(`Clicked at ${data.x}, ${data.y}`);
});
// 类型错误: 参数类型不匹配
// emitter.emit('click', { value: 'wrong' });
// 正确用法
emitter.emit('click', { x: 10, y: 20 });
```
## 结论
TypeScript的类型系统非常强大,掌握这些高级类型技巧可以帮助你编写更安全、更可维护的代码。通过条件类型、映射类型、模板字面量类型、递归类型和类型推断,你可以创建复杂而精确的类型定义,捕获更多潜在错误,并提供更好的开发体验。
随着对这些技术的深入理解,你将能够充分利用TypeScript的类型系统,创建更加健壮的应用程序。