Skip to content

TypeScript 泛型编程

一、泛型基础

1.1 为什么需要泛型

typescript
// 问题:identity 函数需要保持输入输出类型一致
function identity(arg: any): any {
  return arg;
}
// 丢失了类型信息!

// 解决:使用泛型
function identity<T>(arg: T): T {
  return arg;
}

const num = identity<number>(42);   // num: number
const str = identity('hello');      // str: string (类型推断)

1.2 泛型函数

typescript
// 多个类型参数
function pair<T, U>(first: T, second: U): [T, U] {
  return [first, second];
}

const p = pair('hello', 42); // [string, number]

// 泛型箭头函数
const identity2 = <T>(arg: T): T => arg;

// 在 TSX 中需要加逗号避免与 JSX 冲突
const identity3 = <T,>(arg: T): T => arg;

1.3 泛型接口

typescript
interface GenericIdentityFn<T> {
  (arg: T): T;
}

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

const stringContainer: Container<string> = {
  value: 'hello',
  getValue() { return this.value; }
};

1.4 泛型类

typescript
class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);

二、泛型约束

2.1 extends 约束

typescript
// 约束 T 必须有 length 属性
interface Lengthwise {
  length: number;
}

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

logLength('hello');     // ✅ string 有 length
logLength([1, 2, 3]);   // ✅ array 有 length
logLength({ length: 5 }); // ✅
// logLength(123);      // ❌ number 没有 length

2.2 keyof 约束

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

const person = { name: 'Alice', age: 30 };
getProperty(person, 'name'); // ✅ 'Alice'
// getProperty(person, 'email'); // ❌ 'email' 不在 keyof person 中

2.3 多重约束

typescript
interface Named { name: string }
interface Aged { age: number }

// T 必须同时满足 Named 和 Aged
function greet<T extends Named & Aged>(person: T): string {
  return `Hello, ${person.name}, you are ${person.age} years old`;
}

三、条件类型

3.1 基本语法

typescript
// T extends U ? X : Y
type IsString<T> = T extends string ? true : false;

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

3.2 分布式条件类型

typescript
// 当 T 是联合类型时,条件类型会分布到每个成员
type ToArray<T> = T extends any ? T[] : never;

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

// 禁止分布:用 [] 包裹
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Result = ToArrayNonDist<string | number>; // (string | number)[]

3.3 infer 关键字

typescript
// infer 用于在条件类型中推断类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type FnReturn = ReturnType<() => string>; // string

// 提取 Promise 的值类型
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;

type PromiseValue = Awaited<Promise<Promise<string>>>; // string

// 提取数组元素类型
type ElementType<T> = T extends (infer U)[] ? U : never;

type ArrElement = ElementType<number[]>; // number

// 提取函数第一个参数类型
type FirstArg<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;

type First = FirstArg<(a: string, b: number) => void>; // string

四、内置工具类型

4.1 常用工具类型

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

// Required<T> - 所有属性必选
type Required<T> = { [P in keyof T]-?: T[P] };

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

// Pick<T, K> - 选取部分属性
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

// Omit<T, K> - 排除部分属性
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

// Record<K, V> - 构造对象类型
type Record<K extends keyof any, V> = { [P in K]: V };

4.2 联合类型工具

typescript
// Exclude<T, U> - 从 T 中排除 U
type Exclude<T, U> = T extends U ? never : T;
type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'

// Extract<T, U> - 从 T 中提取 U
type Extract<T, U> = T extends U ? T : never;
type T2 = Extract<'a' | 'b' | 'c', 'a' | 'f'>; // 'a'

// NonNullable<T> - 排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;

4.3 函数工具类型

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

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

// 示例
type Fn = (a: string, b: number) => boolean;
type FnParams = Parameters<Fn>;   // [string, number]
type FnReturn = ReturnType<Fn>;   // boolean

五、高频面试题

Q1: 实现 DeepPartial

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

Q2: 实现 DeepReadonly

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

Q3: 泛型的协变与逆变

typescript
// 协变:子类型可赋值给父类型
type Co<T> = () => T; // 返回值协变
declare let coAnimal: Co<Animal>;
declare let coDog: Co<Dog>;
coAnimal = coDog; // ✅ Dog extends Animal

// 逆变:父类型可赋值给子类型
type Contra<T> = (arg: T) => void; // 参数逆变
declare let contraAnimal: Contra<Animal>;
declare let contraDog: Contra<Dog>;
contraDog = contraAnimal; // ✅

前端面试知识库