TypeScript高级类型编程实战:条件类型、映射类型与模板字面量类型

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的高级类型编程能力为开发者提供了强大的工具来创建类型安全、可维护的代码。通过条件类型、映射类型和模板字面量类型的组合使用,我们可以:

  1. 创建复杂的类型约束和转换
  2. 构建类型安全的API和数据结构
  3. 减少运行时错误
  4. 提高代码的可读性和可维护性

虽然这些高级类型技术有一定的学习曲线,但一旦掌握,它们将成为你TypeScript工具箱中不可或缺的部分。建议在实际项目中逐步应用这些技术,从简单的类型工具开始,逐渐过渡到更复杂的类型编程。

© 版权声明
THE END
喜欢就支持一下吧
点赞12 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容