TypeScript高级类型编程实战:条件类型、映射类型与模板字面量类型
TypeScript的类型系统是其最强大的特性之一。除了基本的类型注解,TypeScript还提供了高级类型编程能力,包括条件类型、映射类型和模板字面量类型等。掌握这些高级类型技术,可以让你编写出更加类型安全、可维护的代码。本文将深入探讨这些高级类型,并通过实际示例展示如何在实际项目中应用它们。
1. 条件类型(Conditional Types)
条件类型允许我们根据输入类型的不同选择不同的输出类型,类似于JavaScript中的三元运算符。
// 基础条件类型
type IsString = T extends string ? true : false
type A = IsString // true
type B = IsString // false
type C = IsString<"hello"> // true (字面量类型也是string的子类型)
条件类型在泛型工具类型中非常有用:
// 提取数组元素的类型
type ElementType = T extends (infer U)[] ? U : never
type StringArrayElement = ElementType // string
type NumberArrayElement = ElementType // number
type NotArray = ElementType // never
// 提取Promise的返回值类型
type PromiseType = T extends Promise ? U : never
type StringPromise = PromiseType> // string
async function fetchData(): Promise<{ id: number }> {
return { id: 1 }
}
type FetchDataReturn = PromiseType> // { id: number }
2. 映射类型(Mapped Types)
映射类型允许我们基于现有类型创建新的类型,通过遍历现有类型的属性并应用转换。
// 基础映射类型:将所有属性变为可选
type Partial = {
[P in keyof T]?: T[P]
}
// 将所有属性变为只读
type Readonly = {
readonly [P in keyof T]: T[P]
}
// 将所有属性变为必需
type Required = {
[P in keyof T]-?: T[P]
}
// 实际应用
interface User {
id: number
name: string
email?: string
}
type PartialUser = Partial
// 等价于: { id?: number; name?: string; email?: string }
type ReadonlyUser = Readonly
// 等价于: { readonly id: number; readonly name: string; readonly email?: string }
type RequiredUser = Required
// 等价于: { id: number; name: string; email: string }
更复杂的映射类型示例:
// 将类型的所有属性值转换为特定类型
type ToString = {
[P in keyof T]: string
}
interface Person {
age: number
name: string
isActive: boolean
}
type StringPerson = ToString
// 等价于: { age: string; name: string; isActive: string }
// 过滤出特定类型的属性
type StringKeys = {
[P in keyof T]: T[P] extends string ? P : never
}[keyof T]
type UserStringKeys = StringKeys // "name" | "email"
// 提取所有函数类型的属性
type FunctionPropertyNames = {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T]
interface Component {
render(): void
props: Record
state: Record
}
type ComponentFunctions = FunctionPropertyNames // "render"
3. 模板字面量类型(Template Literal Types)
TypeScript 4.1引入了模板字面量类型,允许我们基于字符串字面量类型创建新的字符串类型。
// 基础模板字面量类型
type EventName = "click" | "hover" | "focus"
type ElementType = "div" | "span" | "button"
type EventHandlerName = `on${Capitalize}`
// 等价于: "onClick" | "onHover" | "onFocus"
type ElementId = `${ElementType}-${number}`
// 等价于: "div-${number}" | "span-${number}" | "button-${number}"
// 实际应用:创建CSS类名类型
type Color = "red" | "blue" | "green"
type Size = "sm" | "md" | "lg"
type Variant = "primary" | "secondary" | "danger"
type ButtonClass = `btn btn-${Variant} btn-${Size} text-${Color}`
// 示例值: "btn btn-primary btn-md text-red"
4. 条件类型与映射类型的组合应用
将条件类型和映射类型结合使用,可以创建非常强大的类型工具。
// 提取对象中所有可选属性的键
type OptionalKeys = {
[K in keyof T]-?: {} extends Pick ? K : never
}[keyof T]
interface Product {
id: number
name: string
price?: number
description?: string
category: string
}
type ProductOptionalKeys = OptionalKeys // "price" | "description"
// 深度只读类型
type DeepReadonly = T extends Function
? T
: T extends object
? { readonly [P in keyof T]: DeepReadonly }
: T
interface NestedObject {
a: number
b: {
c: string
d: {
e: boolean
}
}
f: number[]
}
type ReadonlyNested = DeepReadonly
// 所有层级都是只读的
5. 实际项目应用:API响应类型安全
让我们看一个实际项目中的应用:创建类型安全的API响应处理器。
// 定义API端点类型
type ApiEndpoints = {
"/users": {
GET: {
response: User[]
query: { page?: number; limit?: number }
}
POST: {
response: User
body: { name: string; email: string }
}
}
"/users/:id": {
GET: {
response: User
params: { id: string }
}
PUT: {
response: User
params: { id: string }
body: Partial>
}
DELETE: {
response: { success: boolean }
params: { id: string }
}
}
}
// 提取请求配置类型
type RequestConfig = {
url: T
method: M
} & (ApiEndpoints[T][M] extends { params: infer P }
? { params: P }
: {}) &
(ApiEndpoints[T][M] extends { query: infer Q }
? { query: Q }
: {}) &
(ApiEndpoints[T][M] extends { body: infer B }
? { body: B }
: {})
// 提取响应类型
type ResponseType =
ApiEndpoints[T][M] extends { response: infer R } ? R : never
// 类型安全的API客户端
class ApiClient {
async request(
config: RequestConfig
): Promise> {
// 实际的HTTP请求逻辑
const response = await fetch(this.buildUrl(config), {
method: config.method as string,
headers: {
"Content-Type": "application/json",
},
body: "body" in config ? JSON.stringify(config.body) : undefined,
})
return response.json()
}
private buildUrl(config: any): string {
// URL构建逻辑
return ""
}
}
// 使用示例
const api = new ApiClient()
// 类型安全的使用
const users = await api.request({
url: "/users",
method: "GET",
query: { page: 1, limit: 10 } // 自动类型检查
})
const newUser = await api.request({
url: "/users",
method: "POST",
body: { name: "John", email: "john@example.com" } // 自动类型检查
})
6. 实用类型工具库
以下是一些实用的类型工具,可以在项目中直接使用:
// 1. 获取对象值的联合类型
type ValueOf = T[keyof T]
// 2. 将联合类型转换为元组(需要TypeScript 4.0+)
type UnionToTuple =
(T extends any ? (t: T) => T : never) extends ((...args: infer U) => any)
? U
: never
// 3. 获取函数参数的类型元组
type Parameters = T extends (...args: infer P) => any ? P : never
// 4. 获取函数返回值的类型
type ReturnType = T extends (...args: any[]) => infer R ? R : never
// 5. 构造器类型
type ConstructorParameters any> =
T extends new (...args: infer P) => any ? P : never
type InstanceType any> =
T extends new (...args: any) => infer R ? R : never
// 6. 排除null和undefined
type NonNullable = T extends null | undefined ? never : T
// 7. 提取Promise的返回值类型(递归版本)
type Awaited = T extends PromiseLike ? Awaited : T
// 8. 深度Partial
type DeepPartial = T extends Function
? T
: T extends object
? { [P in keyof T]?: DeepPartial }
: T
总结
TypeScript的高级类型编程能力为开发者提供了强大的工具来创建类型安全、可维护的代码。通过条件类型、映射类型和模板字面量类型的组合使用,我们可以:
- 创建复杂的类型约束和转换
- 构建类型安全的API和数据结构
- 减少运行时错误
- 提高代码的可读性和可维护性
虽然这些高级类型技术有一定的学习曲线,但一旦掌握,它们将成为你TypeScript工具箱中不可或缺的部分。建议在实际项目中逐步应用这些技术,从简单的类型工具开始,逐渐过渡到更复杂的类型编程。
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END




暂无评论内容