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 没有 length2.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>; // false3.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; // ✅