TypeScript 编程 前端

TypeScript进阶技巧与模式

掌握TypeScript的高级特性和设计模式,提升代码质量和开发效率。从泛型到装饰器,从类型守卫到条件类型。

5分钟
592 字
TypeScript进阶技巧与模式

TypeScript 进阶技巧与模式

TypeScript 作为 JavaScript 的超集,为我们提供了强大的类型系统。掌握其高级特性能够显著提升代码质量和开发体验。

高级类型系统

1. 条件类型 (Conditional Types)

条件类型允许我们根据类型条件来选择不同的类型:

typescript
type ApiResponse<T> = T extends string ? { message: T } : { data: T };

type StringResponse = ApiResponse<string>; // { message: string }
type NumberResponse = ApiResponse<number>; // { data: number }

2. 映射类型 (Mapped Types)

映射类型让我们能够基于现有类型创建新类型:

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

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

interface User {
  id: number;
  name: string;
  email?: string;
}

type PartialUser = Partial<User>; // 所有属性都变为可选
type RequiredUser = Required<User>; // 所有属性都变为必需

3. 模板字面量类型

TypeScript 4.1 引入的强大特性:

typescript
type EventName<T extends string> = `on${Capitalize<T>}`;
type ButtonEvents = EventName<"click" | "hover">; // 'onClick' | 'onHover'

type Route<T extends string> = `/api/${T}`;
type UserRoutes = Route<"users" | "posts">; // '/api/users' | '/api/posts'

高级泛型技巧

1. 泛型约束

使用泛型约束来限制类型参数:

typescript
interface Lengthwise {
  length: number;
}

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

// 正确:string有length属性
loggingIdentity("hello");

// 正确:数组有length属性
loggingIdentity([1, 2, 3]);

// 错误:number没有length属性
// loggingIdentity(123);

2. 键值约束

确保对象键的类型安全:

typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const person = { name: "Alice", age: 30, city: "New York" };

const name = getProperty(person, "name"); // string
const age = getProperty(person, "age"); // number
// const invalid = getProperty(person, "invalid"); // 编译错误

3. 条件类型与 infer

使用infer关键字提取类型信息:

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

type ArrayElement<T> = T extends (infer U)[] ? U : never;

function getString(): string {
  return "hello";
}
function getNumbers(): number[] {
  return [1, 2, 3];
}

type StringReturn = ReturnType<typeof getString>; // string
type NumberArray = ArrayElement<number[]>; // number

实用工具类型

1. 深度只读类型

typescript
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

interface Config {
  database: {
    host: string;
    port: number;
  };
  cache: {
    ttl: number;
  };
}

type ReadonlyConfig = DeepReadonly<Config>;
// 所有嵌套属性都变为只读

2. 类型安全的事件发射器

typescript
interface EventMap {
  "user:login": { userId: string; timestamp: Date };
  "user:logout": { userId: string };
  "data:update": { id: string; data: any };
}

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));
    }
  }
}

const emitter = new TypedEventEmitter<EventMap>();

// 类型安全的事件监听
emitter.on("user:login", (data) => {
  console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});

// 类型安全的事件发射
emitter.emit("user:login", {
  userId: "123",
  timestamp: new Date(),
});

装饰器模式

1. 类装饰器

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;
  }
}

2. 方法装饰器

typescript
function measure(
  target: any,
  propertyName: string,
  descriptor: PropertyDescriptor
) {
  const method = descriptor.value;

  descriptor.value = function (...args: any[]) {
    const start = performance.now();
    const result = method.apply(this, args);
    const finish = performance.now();
    console.log(`${propertyName} took ${finish - start} milliseconds`);
    return result;
  };
}

class Calculator {
  @measure
  fibonacci(n: number): number {
    if (n < 2) return n;
    return this.fibonacci(n - 1) + this.fibonacci(n - 2);
  }
}

类型守卫

1. 用户定义的类型守卫

typescript
interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function move(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim(); // TypeScript知道这里pet是Fish类型
  } else {
    pet.fly(); // TypeScript知道这里pet是Bird类型
  }
}

2. 判别联合类型

typescript
interface LoadingState {
  status: "loading";
}

interface SuccessState {
  status: "success";
  data: any;
}

interface ErrorState {
  status: "error";
  error: string;
}

type State = LoadingState | SuccessState | ErrorState;

function handleState(state: State) {
  switch (state.status) {
    case "loading":
      // state的类型被缩窄为LoadingState
      console.log("Loading...");
      break;
    case "success":
      // state的类型被缩窄为SuccessState
      console.log("Data:", state.data);
      break;
    case "error":
      // state的类型被缩窄为ErrorState
      console.log("Error:", state.error);
      break;
  }
}

模块声明与命名空间

1. 声明文件

为第三方库创建类型声明:

typescript
// types/my-library.d.ts
declare module "my-library" {
  export interface Config {
    apiKey: string;
    timeout?: number;
  }

  export function initialize(config: Config): void;
  export function getData<T>(endpoint: string): Promise<T>;
}

2. 全局类型扩展

扩展全局类型:

typescript
declare global {
  interface Window {
    myCustomProperty: string;
  }

  namespace NodeJS {
    interface ProcessEnv {
      NODE_ENV: "development" | "production" | "test";
      API_URL: string;
    }
  }
}

// 现在可以安全使用
window.myCustomProperty = "value";
const apiUrl = process.env.API_URL;

最佳实践

1. 严格模式配置

json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true,
    "noImplicitThis": true
  }
}

2. 类型优先的开发方式

typescript
// 先定义类型
interface UserRepository {
  findById(id: string): Promise<User | null>;
  create(user: CreateUserDto): Promise<User>;
  update(id: string, updates: Partial<User>): Promise<User>;
  delete(id: string): Promise<void>;
}

// 再实现具体逻辑
class DatabaseUserRepository implements UserRepository {
  async findById(id: string): Promise<User | null> {
    // 实现逻辑
  }

  // ... 其他方法实现
}

总结

TypeScript 的高级特性为我们提供了强大的类型安全保障和开发体验。通过掌握这些技巧,我们可以:

  • 编写更安全、更可维护的代码
  • 提升开发效率和代码质量
  • 减少运行时错误
  • 改善团队协作

记住,类型系统是为了帮助我们,而不是阻碍我们。合理使用这些高级特性,能让 TypeScript 成为你最得力的开发伙伴。


继续探索 TypeScript 的世界,你会发现更多令人惊喜的特性和可能性。