TypeScript高级类型编程实战:从基础到类型体操

TypeScript高级类型编程实战:从基础到类型体操

TypeScript的类型系统是其最强大的特性之一。除了基本的类型注解,TypeScript还提供了丰富的高级类型特性,可以让我们在编译时捕获更多错误,编写更安全的代码。本文将深入探讨TypeScript的高级类型编程技巧。

1. 基础类型工具

首先回顾一些基础但强大的类型工具:

// 1. 条件类型
type IsString = T extends string ? true : false;
type Test1 = IsString<"hello">; // true
type Test2 = IsString; // false

// 2. 映射类型
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; }

// 3. 索引访问类型
type UserId = User["id"]; // number
type UserKeys = keyof User; // "id" | "name" | "email"

// 4. 模板字面量类型
type EventName = "click" | "hover" | "submit";
type HandlerName = `on${Capitalize}`;
// "onClick" | "onHover" | "onSubmit"

2. 实用工具类型进阶

// 1. 提取函数返回类型
type ReturnType any> = 
  T extends (...args: any) => infer R ? R : never;

function getUser() {
  return { id: 1, name: "John" };
}

type UserReturn = ReturnType;
// { id: number; name: string }

// 2. 提取函数参数类型
type Parameters any> = 
  T extends (...args: infer P) => any ? P : never;

function createUser(id: number, name: string) {
  return { id, name };
}

type CreateUserParams = Parameters;
// [number, string]

// 3. 构造器类型
type ConstructorParameters any> = 
  T extends new (...args: infer P) => any ? P : never;

type InstanceType any> = 
  T extends new (...args: any) => infer R ? R : never;

class UserClass {
  constructor(public id: number, public name: string) {}
}

type UserConstructorParams = ConstructorParameters;
// [number, string]

type UserInstance = InstanceType;
// UserClass

3. 条件类型与推断

// 1. 分布式条件类型
type ToArray = T extends any ? T[] : never;
type StringOrNumberArray = ToArray;
// string[] | number[]

// 2. 过滤类型
type Filter = T extends U ? T : never;
type StringOnly = Filter;
// string

// 3. 排除类型
type Exclude = T extends U ? never : T;
type NonString = Exclude;
// number | boolean

// 4. 提取类型
type Extract = T extends U ? T : never;
type StringOrNumber = Extract;
// string | number

// 5. 非空类型
type NonNullable = T extends null | undefined ? never : T;
type ValidValue = NonNullable;
// string

4. 递归类型与深度操作

// 1. 深度Partial
type DeepPartial = {
  [P in keyof T]?: T[P] extends object ? DeepPartial : T[P];
};

interface NestedObject {
  user: {
    id: number;
    profile: {
      name: string;
      age: number;
    };
  };
  settings: {
    theme: string;
    notifications: boolean;
  };
}

type PartialNested = DeepPartial;
// 所有层级都变为可选

// 2. 深度Readonly
type DeepReadonly = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly : T[P];
};

// 3. 深度Required
type DeepRequired = {
  [P in keyof T]-?: T[P] extends object ? DeepRequired : T[P];
};

// 4. 类型安全的路径访问
type PathImpl = 
  K extends string
    ? T[K] extends object
      ? `${K}.${PathImpl}`
      : K
    : never;

type Path = PathImpl | keyof T;

type UserPaths = Path;
// "user" | "settings" | "user.id" | "user.profile" | "user.profile.name" | "user.profile.age" | "settings.theme" | "settings.notifications"

// 5. 根据路径获取类型
type Get = 
  P extends `${infer K}.${infer Rest}`
    ? K extends keyof T
      ? Get
      : never
    : P extends keyof T
      ? T[P]
      : never;

type UserProfileName = Get; // string
type SettingsTheme = Get; // string

5. 类型体操实战

// 1. 实现Promise.all的类型安全版本
declare function PromiseAll(
  values: readonly [...T]
): Promise<{
  [K in keyof T]: T[K] extends Promise ? R : T[K];
}>;

// 使用示例
const results = PromiseAll([
  Promise.resolve(1),
  Promise.resolve("hello"),
  true
]);
// Promise<[number, string, boolean]>

// 2. 实现柯里化函数的类型
type Curry any> = 
  F extends (...args: infer A) => infer R
    ? A extends [infer First, ...infer Rest]
      ? (arg: First) => Curry<(...args: Rest) => R>
      : R
    : never;

function curry any>(fn: F): Curry {
  return function curried(...args: any[]): any {
    if (args.length >= fn.length) {
      return fn(...args);
    }
    return (...moreArgs: any[]) => curried(...args, ...moreArgs);
  } as Curry;
}

// 使用示例
function add(a: number, b: number, c: number): number {
  return a + b + c;
}

const curriedAdd = curry(add);
// (arg: number) => (arg: number) => (arg: number) => number

const add5 = curriedAdd(5);
// (arg: number) => (arg: number) => number

const add5And10 = add5(10);
// (arg: number) => number

const result = add5And10(15); // 30

// 3. 实现路由参数解析
type ParseRouteParams = 
  T extends `${infer Start}:${infer Param}/${infer Rest}`
    ? { [K in Param | keyof ParseRouteParams]: string }
    : T extends `${infer Start}:${infer Param}`
      ? { [K in Param]: string }
      : {};

// 使用示例
type BlogPostParams = ParseRouteParams<"/posts/:id">;
// { id: string }

type UserProfileParams = ParseRouteParams<"/users/:userId/posts/:postId">;
// { userId: string; postId: string }

// 4. 实现类型安全的EventEmitter
type EventMap = {
  click: { x: number; y: number };
  change: { value: string };
  submit: { data: Record };
};

class TypedEventEmitter> {
  private listeners: Map void>> = new Map();
  
  on(event: K, listener: (data: T[K]) => void): void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    this.listeners.get(event)!.push(listener);
  }
  
  emit(event: K, data: T[K]): void {
    const listeners = this.listeners.get(event);
    if (listeners) {
      listeners.forEach(listener => listener(data));
    }
  }
}

// 使用示例
const emitter = new TypedEventEmitter();

emitter.on("click", (data) => {
  console.log(`Clicked at (${data.x}, ${data.y})`);
  // data的类型自动推断为 { x: number; y: number }
});

emitter.emit("click", { x: 100, y: 200 });
// emitter.emit("click", { x: 100 }); // 错误:缺少y属性

6. 实用类型模式

// 1. 品牌类型(Branded Types)
type Brand = K & { readonly __brand: T };

type UserId = Brand;
type ProductId = Brand;

function createUserId(id: number): UserId {
  return id as UserId;
}

function createProductId(id: number): ProductId {
  return id as ProductId;
}

const userId = createUserId(123);
const productId = createProductId(456);

// 以下代码会在编译时报错
// function processUser(id: UserId) { /* ... */ }
// processUser(productId); // 错误:类型不匹配

// 2. 名义类型(Nominal Types)
declare const nominalType: unique symbol;

type Nominal = T & {
  readonly [nominalType]: K;
};

type Email = Nominal;
type Phone = Nominal;

function validateEmail(email: string): Email | null {
  if (/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
    return email as Email;
  }
  return null;
}

// 3. 类型安全的枚举替代方案
const UserRole = {
  ADMIN: "admin",
  USER: "user",
  GUEST: "guest"
} as const;

type UserRole = typeof UserRole[keyof typeof UserRole];
// "admin" | "user" | "guest"

// 4. 类型安全的配置对象
type Config = {
  api: {
    baseUrl: string;
    timeout: number;
  };
  features: {
    darkMode: boolean;
    analytics: boolean;
  };
};

function createConfig(config: T): T {
  return config;
}

const config = createConfig({
  api: {
    baseUrl: "https://api.example.com",
    timeout: 5000
  },
  features: {
    darkMode: true,
    analytics: false
  }
});
// 类型安全,自动推断

总结

TypeScript的高级类型系统提供了强大的工具,可以让我们在编译时捕获更多错误,编写更安全的代码。通过掌握这些高级类型技巧,我们可以:

  1. 创建更精确的类型约束,减少运行时错误
  2. 实现类型安全的API和库
  3. 提高代码的可维护性和可读性
  4. 利用类型系统进行文档化
  5. 实现复杂的类型转换和验证

在实际项目中,建议:

  • 从简单的工具类型开始,逐步学习复杂类型
  • 使用类型体操解决实际问题,而不是为了炫技
  • 保持类型的简洁性,避免过度复杂的类型定义
  • 利用TypeScript的类型推断,减少显式类型注解
  • 定期重构类型定义,保持类型系统的整洁

通过合理运用TypeScript的高级类型特性,我们可以构建出更健壮、更易维护的应用程序。

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

请登录后发表评论

    暂无评论内容