Skip to content

TypeScript 进阶技巧

TypeScript 作为 JavaScript 的超集,提供了强大的类型系统。本文将深入探讨 TypeScript 的高级特性,帮助你写出更加健壮和优雅的代码。

泛型(Generics)

泛型是 TypeScript 最强大的特性之一,它允许我们创建可重用的组件,这些组件可以处理多种类型。

基础泛型

typescript
// 简单的泛型函数
function identity<T>(arg: T): T {
  return arg
}

// 使用
const output1 = identity<string>("myString")
const output2 = identity<number>(123)
const output3 = identity("myString") // 类型推断

泛型约束

使用 extends 关键字来约束泛型类型:

typescript
interface Lengthwise {
  length: number
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length)
  return arg
}

loggingIdentity("hello") // ✅ 字符串有 length 属性
loggingIdentity([1, 2, 3]) // ✅ 数组有 length 属性
loggingIdentity(123) // ❌ 数字没有 length 属性

泛型接口和类

typescript
// 泛型接口
interface GenericInterface<T> {
  value: T
  getValue(): T
}

// 泛型类
class GenericNumber<T> {
  zeroValue: T
  add: (x: T, y: T) => T
}

const myGenericNumber = new GenericNumber<number>()
myGenericNumber.zeroValue = 0
myGenericNumber.add = (x, y) => x + y

条件类型(Conditional Types)

条件类型允许我们根据类型关系选择类型:

typescript
type IsArray<T> = T extends Array<any> ? true : false

type Test1 = IsArray<string[]> // true
type Test2 = IsArray<string> // false

分布式条件类型

typescript
type ToArray<T> = T extends any ? T[] : never

type StrArrOrNumArr = ToArray<string | number> 
// string[] | number[]

infer 关键字

infer 用于在条件类型中推断类型:

typescript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any

type T0 = ReturnType<() => string> // string
type T1 = ReturnType<(x: number) => number> // number

映射类型(Mapped Types)

映射类型允许我们基于旧类型创建新类型:

typescript
// 将所有属性变为可选
type Partial<T> = {
  [P in keyof T]?: T[P]
}

// 将所有属性变为只读
type Readonly<T> = {
  readonly [P in keyof T]: T[P]
}

// 将所有属性变为必需
type Required<T> = {
  [P in keyof T]-?: T[P]
}

实际应用

typescript
interface User {
  name: string
  age: number
  email: string
}

// 创建所有属性可选的新类型
type PartialUser = Partial<User>
// { name?: string; age?: number; email?: string }

// 创建只读版本
type ReadonlyUser = Readonly<User>
// { readonly name: string; readonly age: number; readonly email: string }

工具类型(Utility Types)

TypeScript 提供了许多内置的工具类型:

Pick 和 Omit

typescript
interface Todo {
  title: string
  description: string
  completed: boolean
}

// Pick: 选择部分属性
type TodoPreview = Pick<Todo, "title" | "completed">
// { title: string; completed: boolean }

// Omit: 排除部分属性
type TodoInfo = Omit<Todo, "completed" | "description">
// { title: string }

Record

typescript
// Record<K, T> 创建一个对象类型,其键为 K,值为 T
type PageInfo = {
  title: string
}

type Page = "home" | "about" | "contact"

const x: Record<Page, PageInfo> = {
  home: { title: "Home" },
  about: { title: "About" },
  contact: { title: "Contact" }
}

Exclude 和 Extract

typescript
type T0 = Exclude<"a" | "b" | "c", "a"> // "b" | "c"
type T1 = Extract<"a" | "b" | "c", "a" | "f"> // "a"

模板字面量类型

TypeScript 4.1 引入了模板字面量类型:

typescript
type World = "world"
type Greeting = `hello ${World}` // "hello world"

type EmailLocaleIDs = "welcome_email" | "email_heading"
type FooterLocaleIDs = "footer_title" | "footer_sendoff"
type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`
// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"

类型守卫(Type Guards)

类型守卫帮助我们缩小类型范围:

typescript
// typeof 类型守卫
function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value
  }
  return padding + value
}

// instanceof 类型守卫
class Bird {
  fly() {
    console.log("flying")
  }
}

function move(animal: Bird | Fish) {
  if (animal instanceof Bird) {
    animal.fly()
  }
}

// 自定义类型守卫
function isString(value: unknown): value is string {
  return typeof value === "string"
}

装饰器(Decorators)

装饰器是一种特殊类型的声明,可以附加到类、方法、属性等:

typescript
function sealed(constructor: Function) {
  Object.seal(constructor)
  Object.seal(constructor.prototype)
}

@sealed
class BugReport {
  type = "report"
  title: string

  constructor(t: string) {
    this.title = t
  }
}

实际应用场景

API 响应类型

typescript
// 定义 API 响应类型
type ApiResponse<T> = {
  success: true
  data: T
} | {
  success: false
  error: string
}

async function fetchUser(id: number): Promise<ApiResponse<User>> {
  try {
    const user = await api.getUser(id)
    return { success: true, data: user }
  } catch (error) {
    return { success: false, error: error.message }
  }
}

表单验证

typescript
type ValidationResult<T> = {
  [K in keyof T]?: string
}

function validate<T>(data: T, rules: ValidationRules<T>): ValidationResult<T> {
  // 验证逻辑
  return {}
}

最佳实践

  1. 充分利用类型推断:让 TypeScript 自动推断类型,减少冗余
  2. 使用联合类型和字面量类型:提高类型精确度
  3. 合理使用泛型:提高代码复用性
  4. 利用工具类型:减少重复的类型定义
  5. 编写类型守卫:提高类型安全性

总结

TypeScript 的高级特性为我们提供了强大的类型系统,帮助我们构建更加健壮的应用程序。通过合理使用泛型、条件类型、映射类型等特性,我们可以写出更加优雅和可维护的代码。掌握这些进阶技巧,将大大提升你的 TypeScript 开发能力。


辛田信息技术 · 内部技术分享 · 仅供学习与参考