TypeScript高级技巧:泛型与类型体操

大家好,我是欧阳瑞(Rich Own)。今天想和大家聊聊TypeScript的高级特性,特别是泛型和类型体操。作为一个全栈开发者,我发现TypeScript的类型系统非常强大,掌握好这些技巧可以大大提高代码的健壮性和可维护性。

什么是泛型?

泛型是TypeScript中一种强大的工具,允许我们编写可重用的组件,同时保持类型安全。

基础泛型

泛型函数

function identity<T>(arg: T): T {
  return arg;
}

// 使用
const num = identity<number>(42);
const str = identity<string>("hello");

泛型接口

interface Container<T> {
  value: T;
  getValue: () => T;
}

const numberContainer: Container<number> = {
  value: 42,
  getValue: () => 42
};

泛型类

class Box<T> {
  private items: T[] = [];
  
  add(item: T): void {
    this.items.push(item);
  }
  
  get(index: number): T {
    return this.items[index];
  }
}

泛型约束

使用接口约束

interface Lengthwise {
  length: number;
}

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

logLength("hello"); // 5
logLength([1, 2, 3]); // 3
logLength({ length: 10, value: "test" }); // 10

类型参数约束

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

const person = { name: "Alice", age: 30 };
const name = getProperty(person, "name"); // string
const age = getProperty(person, "age"); // number

条件类型

基础条件类型

type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false

分布式条件类型

type Flatten<T> = T extends Array<infer U> ? U : T;

type Flattened = Flatten<number[]>; // number
type NotFlattened = Flatten<string>; // string

infer关键字

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

type Func = () => string;
type Result = ReturnType<Func>; // string

类型体操实战

1. 实现Pick

type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

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

type UserName = MyPick<User, "name">; // { name: string }

2. 实现Readonly

type MyReadonly<T> = {
  readonly [P in keyof T]: T[P];
};

interface Config {
  apiUrl: string;
  timeout: number;
}

type ReadonlyConfig = MyReadonly<Config>;

3. 实现TupleToObject

type TupleToObject<T extends readonly any[]> = {
  [P in T[number]]: P;
};

const tuple = ["a", "b", "c"] as const;
type ObjectType = TupleToObject<typeof tuple>; // { a: "a", b: "b", c: "c" }

4. 实现First

type First<T extends any[]> = T extends [infer U, ...any[]] ? U : never;

type A = First<[1, 2, 3]>; // 1
type B = First<[]>; // never

5. 实现Length

type Length<T extends readonly any[]> = T["length"];

type A = Length<[1, 2, 3]>; // 3
type B = Length<[]>; // 0

映射类型

基础映射类型

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

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

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

type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

条件映射类型

type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};

type NullableUser = Nullable<User>; // { id: number | null; name: string | null; age: number | null }

模板字面量类型

基础模板字面量

type Color = "red" | "green" | "blue";
type Size = "small" | "medium" | "large";

type ColorSize = `${Color}-${Size}`; // "red-small" | "red-medium" | "red-large" | ...

转换类型

type Uppercase<T extends string> = `${Uppercase<T>}`;
type Lowercase<T extends string> = `${Lowercase<T>}`;
type Capitalize<T extends string> = `${Capitalize<T>}`;
type Uncapitalize<T extends string> = `${Uncapitalize<T>}`;

type A = Uppercase<"hello">; // "HELLO"
type B = Capitalize<"hello">; // "Hello"

递归类型

深度Readonly

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

interface NestedObject {
  a: {
    b: {
      c: number;
    };
  };
}

type ReadonlyNested = DeepReadonly<NestedObject>;

深度Partial

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

工具类型实战

实现Promise类型提取

type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;

type A = UnwrapPromise<Promise<string>>; // string
type B = UnwrapPromise<Promise<Promise<number>>>; // number

实现函数重载类型

type OverloadUnion<T> = T extends { (...args: infer A): infer R } ? (...args: A) => R : never;

type Fn = {
  (a: string): number;
  (a: number): string;
};

type Result = OverloadUnion<Fn>; // ((a: string) => number) | ((a: number) => string)

实际应用场景

1. 类型安全的状态管理

type Action<T> = {
  type: string;
  payload: T;
};

type Reducer<S, A> = (state: S, action: A) => S;

function createStore<S, A extends Action<any>>(
  reducer: Reducer<S, A>,
  initialState: S
) {
  let state = initialState;
  
  return {
    getState: () => state,
    dispatch: (action: A) => {
      state = reducer(state, action);
    }
  };
}

2. 类型安全的API调用

type ApiResponse<T> = {
  data: T;
  status: number;
  message: string;
};

async function fetchApi<T>(url: string): Promise<ApiResponse<T>> {
  const response = await fetch(url);
  return response.json();
}

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

const userResponse = await fetchApi<User>("/api/user");
const user: User = userResponse.data;

3. 类型安全的事件系统

type EventMap = {
  "user:created": { userId: number; name: string };
  "user:updated": { userId: number; changes: Partial<User> };
  "system:error": { message: string; code: number };
};

class EventEmitter {
  private listeners: {
    [K in keyof EventMap]?: ((event: EventMap[K]) => void)[];
  } = {};
  
  on<K extends keyof EventMap>(event: K, listener: (event: EventMap[K]) => void) {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event]!.push(listener);
  }
  
  emit<K extends keyof EventMap>(event: K, data: EventMap[K]) {
    this.listeners[event]?.forEach(listener => listener(data));
  }
}

总结

TypeScript的类型系统非常强大,掌握泛型和类型体操可以让你写出更加健壮和可维护的代码。从简单的泛型函数到复杂的类型体操,每一步都在提升代码的类型安全。

我的鬃狮蜥Hash对TypeScript也有自己的理解——它总是根据不同的蟋蟀类型选择不同的捕食策略,这也许就是自然界的"类型推断"吧!

如果你有TypeScript方面的问题,欢迎留言交流!我是欧阳瑞,极客之路,永无止境!


技术栈:TypeScript · 泛型 · 类型体操

更多推荐