TypeScript类型体操:掌握高级泛型和类型操作技巧
本文详细介绍TypeScript的高级类型特性,包括泛型、条件类型、映射类型等,通过实例展示类型编程的魅力。
TypeScript的类型系统是其最强大的特性之一,它不仅仅能提供类型安全,还能像编程一样操作类型。通过泛型、条件类型、映射类型等高级特性,我们可以创建出既灵活又类型安全的代码。本文将带你深入TypeScript的类型世界,掌握这些高级技巧。
泛型基础#
泛型函数#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // 基础泛型函数
function identity<T>(arg: T): T {
return arg
}
// 使用
const num = identity(10) // 类型是 number
const str = identity('hello') // 类型是 string
// 泛型约束
function logLength<T extends { length: number }>(arg: T) {
console.log(arg.length)
}
logLength('hello') // OK
logLength([1, 2, 3]) // OK
// logLength(10) // Error: number 没有 length 属性
|
泛型接口#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| interface Repository<T> {
findById(id: string): Promise<T | null>
save(entity: T): Promise<T>
delete(id: string): Promise<void>
}
interface User {
id: string
name: string
email: string
}
class UserRepository implements Repository<User> {
async findById(id: string): Promise<User | null> {
// 实现
return null
}
async save(user: User): Promise<User> {
// 实现
return user
}
async delete(id: string): Promise<void> {
// 实现
}
}
|
泛型类#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| class Box<T> {
private content: T
constructor(value: T) {
this.content = value
}
getValue(): T {
return this.content
}
setValue(value: T): void {
this.content = value
}
}
const numberBox = new Box(123)
const stringBox = new Box('hello')
// 泛型类继承
class NumberBox extends Box<number> {
add(num: number): void {
this.setValue(this.getValue() + num)
}
}
|
高级泛型技巧#
1. 多个泛型参数#
1
2
3
4
5
6
7
8
9
10
11
| function pair<T, U>(first: T, second: U): [T, U] {
return [first, second]
}
function map<T, U>(array: T[], fn: (item: T) => U): U[] {
return array.map(fn)
}
// 使用
const [a, b] = pair('hello', 123)
const lengths = map(['a', 'bb', 'ccc'], s => s.length)
|
2. 默认泛型参数#
1
2
3
4
5
6
7
8
9
10
11
12
13
| interface ApiResponse<T = any> {
data: T
status: number
message: string
}
interface UserResponse extends ApiResponse<User> {
// 继承并指定具体类型
}
interface GenericResponse extends ApiResponse {
// 使用默认类型 T = any
}
|
3. 泛型工厂函数#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| function createRepository<T>(): Repository<T> {
return new (class implements Repository<T> {
async findById(id: string): Promise<T | null> {
throw new Error('Not implemented')
}
async save(entity: T): Promise<T> {
throw new Error('Not implemented')
}
async delete(id: string): Promise<void> {
throw new Error('Not implemented')
}
})()
}
const userRepo = createRepository<User>()
|
条件类型#
基础条件类型#
1
2
3
4
5
6
7
8
9
10
11
| // 基础语法
type IsString<T> = T extends string ? true : false
type Test1 = IsString<string> // true
type Test2 = IsString<number> // false
// 实际应用
type NonNullable<T> = T extends null | undefined ? never : T
type Test3 = NonNullable<string | null> // string
type Test4 = NonNullable<number> // number
|
分布式条件类型#
1
2
3
4
5
6
7
8
9
| // 联合类型会被分布处理
type ToArray<T> = T extends any ? T[] : never
type Test5 = ToArray<string | number> // string[] | number[]
// 过滤联合类型
type FilterString<T> = T extends string ? T : never
type Test6 = FilterString<string | number | boolean> // string
|
推断关键字 infer#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never
function getString(): string {
return 'hello'
}
type Test7 = ReturnType<typeof getString> // string
// 提取数组元素类型
type ElementOf<T> = T extends (infer E)[] ? E : never
type Test8 = ElementOf<string[]> // string
// 提取Promise的值类型
type UnboxPromise<T> = T extends Promise<infer U> ? U : never
type Test9 = UnboxPromise<string> // string
type Test10 = UnboxPromise<string> | number // string | number
|
映射类型#
基础映射类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| // 将所有属性变为可选
type Partial<T> = {
[P in keyof T]?: T[P]
}
// 将所有属性变为必需
type Required<T> = {
[P in keyof T]-?: T[P]
}
// 只读映射
type Readonly<T> = {
readonly [P in keyof T]: T[P]
}
|
自定义映射类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // 选择特定属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
// 排除特定属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
// 将所有属性转换为另一种类型
type StringifyProperties<T> = {
[K in keyof T]: T[K] extends string ? T[K] : string
}
interface User {
id: number
name: string
age: number
}
type Test11 = StringifyProperties<User>
// {
// id: string
// name: string
// age: string
// }
|
条件映射#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| // 根据类型条件进行不同的映射
type ConditionalMap<T> = {
[K in keyof T]: T[K] extends string
? string
: T[K] extends number
? number
: never
}
// 添加或修改属性
type WithDefaults<T> = T & {
id: string
createdAt: Date
}
// 删除某些属性
type DeleteProperties<T, K extends keyof T> = Omit<T, K>
|
实用的高级类型#
1. 深度只读#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P]
}
interface Nested {
a: {
b: {
c: number
}
}
}
type Test12 = DeepReadonly<Nested>
// 所有嵌套属性都变为只读
|
2. 函数参数提取#
1
2
3
4
5
6
7
8
9
10
11
12
| type FirstParameter<T> = T extends (arg1: infer U, ...args: any[]) => any
? U
: never
type AllParameters<T> = T extends (...args: infer U) => any
? U
: never
function example(a: number, b: string, c: boolean): void {}
type Test13 = FirstParameter<typeof example> // number
type Test14 = AllParameters<typeof example> // [number, string, boolean]
|
3. 联合类型转交叉类型#
1
2
3
4
5
6
7
8
| type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends
(k: infer I) => void
? I
: never
type Test15 = UnionToIntersection<{ a: string } | { b: number }>
// { a: string } & { b: number }
|
4. 获取对象值的类型#
1
2
3
4
5
6
7
8
9
| type ValueOf<T> = T[keyof T]
interface Config {
apiUrl: string
timeout: number
retries: number
}
type Test16 = ValueOf<Config> // string | number
|
类型守卫和谓词#
类型谓词#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| function isString(value: unknown): value is string {
return typeof value === 'string'
}
function processValue(value: unknown) {
if (isString(value)) {
// 这里 value 的类型被收窄为 string
console.log(value.toUpperCase())
}
}
// 更复杂的类型谓词
function isUser(obj: any): obj is User {
return (
obj &&
typeof obj === 'object' &&
'id' in obj &&
'name' in obj &&
'email' in obj
)
}
|
自定义类型守卫#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| interface Cat {
type: 'cat'
meow(): void
}
interface Dog {
type: 'dog'
bark(): void
}
type Animal = Cat | Dog
function isCat(animal: Animal): animal is Cat {
return animal.type === 'cat'
}
function handleAnimal(animal: Animal) {
if (isCat(animal)) {
animal.meow() // TypeScript 知道这是 Cat
} else {
animal.bark() // TypeScript 知道这是 Dog
}
}
|
模板字面量类型#
基础用法#
1
2
3
4
5
6
7
8
| type EventName = `on${Capitalize<string>}`
type Test17 = EventName // "onUppercase<string>" 不是我们想要的
// 实际应用
type ButtonEvent = `on${'click' | 'hover' | 'focus'}`
type Test18 = ButtonEvent // "onclick" | "onhover" | "onfocus"
|
实用工具类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| // 首字母大写
type Capitalize<T extends string> =
T extends `${infer F}${infer R}`
? `${Uppercase<F>}${R}`
: T
type Test19 = Capitalize<'hello'> // "Hello"
// 驼峰命名转换
type CamelCase<S extends string> =
S extends `${infer P1}_${infer P2}${infer P3}`
? `${P1}${Uppercase<P2>}${CamelCase<P3>}`
: S
type Test20 = CamelCase<'hello_world_test'> // "helloWorldTest"
|
实战应用#
1. API类型生成#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
| // 基于端点生成类型
type ApiMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'
interface ApiEndpoint {
method: ApiMethod
path: string
response: any
request?: any
}
type EndpointTypes<T extends ApiEndpoint> = {
[K in keyof T]: T[K] extends { method: infer M; path: infer P; response: infer R }
? M extends 'GET'
? (params?: Record<string, any>) => Promise<R>
: (data: T[K]['request'], params?: Record<string, any>) => Promise<R>
: never
}
// 使用示例
type Api = {
getUser: ApiEndpoint & {
method: 'GET'
path: '/users/:id'
response: User
}
createUser: ApiEndpoint & {
method: 'POST'
path: '/users'
request: Omit<User, 'id'>
response: User
}
}
type ApiMethods = EndpointTypes<Api>
/*
{
getUser: (params?: Record<string, any>) => Promise<User>
createUser: (data: Omit<User, 'id'>, params?: Record<string, any>) => Promise<User>
}
*/
|
2. 状态管理类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // 状态机类型
type State = 'idle' | 'loading' | 'success' | 'error'
type StateActions<T> = {
[K in keyof T]: (payload: T[K]) => void
}
type StateMachine<S extends State, A extends Record<string, any>> = {
state: S
dispatch: <K extends keyof A>(action: K, payload: A[K]) => void
}
// 使用示例
type UserState = StateMachine<
'loading' | 'success' | 'error',
{
setUser: User
setError: string
}
>
|
3. 表单验证类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| type ValidationRule<T> = {
required?: boolean
minLength?: number
maxLength?: number
pattern?: RegExp
custom?: (value: T) => boolean | string
}
type ValidationSchema<T extends Record<string, any>> = {
[K in keyof T]: ValidationRule<T[K]>
}
type ValidationResult<T> = {
[K in keyof T]?: string
}
// 使用示例
interface LoginForm {
email: string
password: string
}
const loginSchema: ValidationSchema<LoginForm> = {
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
},
password: {
required: true,
minLength: 8
}
}
|
性能考虑#
避免过深的递归#
1
2
3
4
5
6
7
8
9
10
11
12
| // ❌ 可能导致性能问题
type DeepNested<T> = T extends object
? { [K in keyof T]: DeepNested<T[K]> }
: T
// ✅ 限制递归深度
type DeepNestedLimited<T, Depth extends number = 0> =
Depth extends 5
? T
: T extends object
? { [K in keyof T]: DeepNestedLimited<T[K], Depth | 0> }
: T
|
使用类型别名优化#
1
2
3
4
5
6
| // ❌ 重复计算复杂类型
type ComplexType<T> = Exclude<keyof T, 'id' | 'createdAt' | 'updatedAt'>
// ✅ 使用中间类型
type BaseModelKeys = 'id' | 'createdAt' | 'updatedAt'
type ComplexType<T> = Exclude<keyof T, BaseModelKeys>
|
TypeScript的高级类型特性为我们提供了强大的工具:
核心概念:
- 泛型:创建可复用的类型安全代码
- 条件类型:基于类型的条件逻辑
- 映射类型:转换现有类型
- 类型守卫:运行时类型检查
最佳实践:
- 合理使用泛型约束
- 利用类型推导简化代码
- 创建可复用的工具类型
- 注意类型系统的性能限制
掌握这些高级类型技巧,将让你能够构建更安全、更灵活的TypeScript应用。
相关资源: