TypeScript 高级类型编程实战

(7 个月前)
Nick Chen
约 5 分钟

TypeScript 的类型系统不仅仅是为了添加类型注解,它实际上是一个功能强大的编程语言。本文将带你深入探索 TypeScript 的高级类型编程技巧。

条件类型 (Conditional Types)

条件类型是 TypeScript 类型系统中最强大的功能之一:

// 基础条件类型
type IsArray<T> = T extends any[] ? true : false;

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

// 更复杂的条件类型
type NonNullable<T> = T extends null | undefined ? never : T;

type SafeString = NonNullable<string | null>; // string

分布式条件类型

当条件类型作用于联合类型时,会产生分布式效果:

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

type ArrayUnion = ToArray<string | number>; // string[] | number[]

映射类型 (Mapped Types)

映射类型允许你基于现有类型创建新类型:

// 基础映射类型
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type Partial<T> = {
  [P in keyof T]?: T[P];
};

// 自定义映射类型
type Getters<T> = {
  [P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};

interface User {
  name: string;
  age: number;
}

type UserGetters = Getters<User>;
// {
//   getName: () => string;
//   getAge: () => number;
// }

键重映射

TypeScript 4.1 引入了键重映射功能:

type EventHandlers<T> = {
  [P in keyof T as `on${Capitalize<string & P>}Changed`]: (value: T[P]) => void;
};

type UserEventHandlers = EventHandlers<User>;
// {
//   onNameChanged: (value: string) => void;
//   onAgeChanged: (value: number) => void;
// }

模板字面量类型

模板字面量类型提供了强大的字符串操作能力:

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"

// 实际应用:API 路径类型
type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type APIPath = '/users' | '/posts' | '/comments';

type APIEndpoint = `${HTTPMethod} ${APIPath}`;
// "GET /users" | "POST /users" | ... 等 12 种组合

字符串操作工具类型

// 内置字符串操作类型
type UppercaseGreeting = Uppercase<"hello world">; // "HELLO WORLD"
type LowercaseGreeting = Lowercase<"HELLO WORLD">; // "hello world"
type CapitalizedGreeting = Capitalize<"hello world">; // "Hello world"
type UncapitalizedGreeting = Uncapitalize<"Hello World">; // "hello World"

递归类型

TypeScript 支持递归类型定义:

// 深度只读类型
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// JSON 类型
type JSONValue =
  | string
  | number
  | boolean
  | null
  | JSONObject
  | JSONArray;

interface JSONObject {
  [key: string]: JSONValue;
}

interface JSONArray extends Array<JSONValue> {}

// 路径类型(支持嵌套对象)
type PathImpl<T, K extends keyof T> = K extends string
  ? T[K] extends Record<string, any>
    ? T[K] extends ArrayLike<any>
      ? K | `${K}.${PathImpl<T[K], Exclude<keyof T[K], keyof any[]>>}`
      : K | `${K}.${PathImpl<T[K], keyof T[K]>}`
    : K
  : never;

type Path<T> = PathImpl<T, keyof T> | keyof T;

// 使用示例
interface NestedObject {
  user: {
    profile: {
      name: string;
      settings: {
        theme: string;
      };
    };
  };
  posts: Array<{
    title: string;
    content: string;
  }>;
}

type ValidPaths = Path<NestedObject>;
// "user" | "posts" | "user.profile" | "user.profile.name" | "user.profile.settings" | "user.profile.settings.theme"

工具类型实战

创建一个类型安全的事件发射器

// 定义事件映射
interface EventMap {
  'user:login': { userId: string; timestamp: number };
  'user:logout': { userId: string };
  'post:created': { postId: string; authorId: string };
  'post:deleted': { postId: string };
}

// 类型安全的事件发射器
class TypedEventEmitter<T extends Record<string, any>> {
  private listeners: {
    [K in keyof T]?: Array<(data: T[K]) => void>;
  } = {};

  on<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event]!.push(listener);
  }

  emit<K extends keyof T>(event: K, data: T[K]): void {
    const eventListeners = this.listeners[event];
    if (eventListeners) {
      eventListeners.forEach(listener => listener(data));
    }
  }

  off<K extends keyof T>(event: K, listener: (data: T[K]) => void): void {
    const eventListeners = this.listeners[event];
    if (eventListeners) {
      const index = eventListeners.indexOf(listener);
      if (index > -1) {
        eventListeners.splice(index, 1);
      }
    }
  }
}

// 使用
const emitter = new TypedEventEmitter<EventMap>();

emitter.on('user:login', (data) => {
  // data 的类型是 { userId: string; timestamp: number }
  console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});

// 类型检查:这会产生编译错误
// emitter.emit('user:login', { userId: 'abc' }); // Error: timestamp 缺失

函数重载的类型安全实现

// 函数重载类型
type Overload = {
  (x: string): string;
  (x: number): number;
  (x: boolean): boolean;
};

// 使用条件类型实现
type SafeOverload<T> = T extends string
  ? string
  : T extends number
  ? number
  : T extends boolean
  ? boolean
  : never;

function safeConvert<T extends string | number | boolean>(x: T): SafeOverload<T> {
  if (typeof x === 'string') {
    return x.toUpperCase() as SafeOverload<T>;
  }
  if (typeof x === 'number') {
    return (x * 2) as SafeOverload<T>;
  }
  if (typeof x === 'boolean') {
    return !x as SafeOverload<T>;
  }
  throw new Error('Unsupported type');
}

const result1 = safeConvert('hello'); // string
const result2 = safeConvert(42);      // number
const result3 = safeConvert(true);    // boolean

性能注意事项

在使用高级类型时,需要注意以下几点:

  1. 避免过度复杂的递归类型:可能导致编译器性能问题
  2. 合理使用条件类型:过多的嵌套条件可能影响 IDE 性能
  3. 缓存复杂的类型计算:使用类型别名避免重复计算
// 不好的做法
type BadExample<T> = T extends object
  ? T extends Array<infer U>
    ? Array<BadExample<U>>
    : { [K in keyof T]: BadExample<T[K]> }
  : T;

// 更好的做法
type ProcessedObject<T> = { [K in keyof T]: Transform<T[K]> };
type ProcessedArray<T> = T extends Array<infer U> ? Array<Transform<U>> : never;

type Transform<T> = T extends object
  ? T extends any[]
    ? ProcessedArray<T>
    : ProcessedObject<T>
  : T;

实际应用案例

表单验证类型

type ValidationRule<T> = {
  required?: boolean;
  minLength?: T extends string ? number : never;
  maxLength?: T extends string ? number : never;
  min?: T extends number ? number : never;
  max?: T extends number ? number : never;
  pattern?: T extends string ? RegExp : never;
};

type FormSchema<T> = {
  [K in keyof T]: ValidationRule<T[K]>;
};

interface UserForm {
  username: string;
  age: number;
  email: string;
}

const userFormSchema: FormSchema<UserForm> = {
  username: {
    required: true,
    minLength: 3,
    maxLength: 20
  },
  age: {
    required: true,
    min: 18,
    max: 120
  },
  email: {
    required: true,
    pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  }
};

总结

TypeScript 的高级类型系统为我们提供了强大的工具来创建更安全、更灵活的代码。通过掌握条件类型、映射类型、模板字面量类型等高级特性,我们可以:

  • 创建更精确的类型定义
  • 提高代码的类型安全性
  • 减少运行时错误
  • 改善开发体验

记住,类型编程的目标是让代码更安全和更易维护,而不是炫技。合理使用这些高级特性,会让你的 TypeScript 代码更上一层楼!


推荐阅读: