Skip to content

TypeScript 泛型

泛型(Generics)是 TypeScript 中非常重要的特性,它允许我们在定义函数、接口或类时不预先指定具体的类型,而在使用时再指定类型的一种特性。

为什么需要泛型?

问题场景

假设我们需要一个函数,可以返回任何类型的值:

ts
// 不使用泛型的方式
function getValue(arg: any): any {
    return arg;
}

const value1 = getValue(123);      // 类型是 any
const value2 = getValue('hello');  // 类型是 any

这种方式的问题是:

  • 失去了类型检查的优势
  • 无法在编译时捕获类型错误
  • 代码提示不准确

泛型的解决方案

使用泛型可以解决这些问题:

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

const value1 = getValue(123);      // 类型是 number
const value2 = getValue('hello');  // 类型是 string

泛型函数

基础语法

ts
// 函数名后添加 <T>,T 是类型变量(可以任意命名)
function identity<T>(arg: T): T {
    return arg;
}

// 使用方式1:显式指定类型
const result1 = identity<string>('hello');

// 使用方式2:让 TypeScript 自动推断类型(推荐)
const result2 = identity(123); // 类型推断为 number

多个泛型参数

ts
function map<I, O>(arr: I[], fn: (item: I) => O): O[] {
    return arr.map(fn);
}

const numbers = [1, 2, 3];
const strings = map(numbers, item => item.toString()); // ['1', '2', '3']

泛型约束(extends)

使用 extends 关键字来限制泛型参数的类型:

ts
// 限制 T 必须包含 length 属性
function getLength<T extends { length: number }>(arg: T): number {
    return arg.length;
}

getLength('hello');        // ✅ 字符串有 length 属性
getLength([1, 2, 3]);      // ✅ 数组有 length 属性
getLength({ length: 5 });  // ✅ 对象有 length 属性
getLength(123);            // ❌ 数字没有 length 属性

使用 keyof 约束

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

const person = { name: 'Tom', age: 20 };
getProperty(person, 'name');  // ✅ 正确
getProperty(person, 'age');   // ✅ 正确
getProperty(person, 'sex');   // ❌ 错误:'sex' 不存在

泛型默认值

ts
function createArray<T = string>(length: number, value: T): T[] {
    return Array(length).fill(value);
}

createArray(3, 'hello');  // string[]
createArray<number>(3, 0); // number[]

泛型接口

基础用法

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

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

const myIdentity: GenericIdentityFn<number> = identity;

泛型接口示例

ts
interface ApiResponse<T> {
    code: number;
    message: string;
    data: T;
}

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

// 使用泛型接口
const userResponse: ApiResponse<User> = {
    code: 200,
    message: 'success',
    data: { id: 1, name: 'Tom' }
};

多个泛型参数

ts
interface KeyValuePair<K, V> {
    key: K;
    value: V;
}

const pair1: KeyValuePair<string, number> = { key: 'age', value: 20 };
const pair2: KeyValuePair<number, string> = { key: 1, value: 'Tom' };

泛型类

基础用法

ts
class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

const myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

const stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = '';
stringNumeric.add = function(x, y) { return x + y; };

类中的泛型方法

ts
class DataStorage<T> {
    private data: T[] = [];

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

    getItem(index: number): T {
        return this.data[index];
    }

    getAllItems(): T[] {
        return this.data;
    }
}

// 使用字符串类型的存储
const stringStorage = new DataStorage<string>();
stringStorage.addItem('hello');
stringStorage.addItem('world');

// 使用数字类型的存储
const numberStorage = new DataStorage<number>();
numberStorage.addItem(1);
numberStorage.addItem(2);

泛型类型别名

ts
type Nullable<T> = T | null;
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Partial<T> = { [P in keyof T]?: T[P] };

// 使用
type User = {
    id: number;
    name: string;
};

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

映射类型中的泛型

ts
// 将对象的所有属性转换为可选
type Partial<T> = {
    [P in keyof T]?: T[P];
};

// 将对象的所有属性转换为只读
type Readonly<T> = {
    readonly [P in keyof T]: T[P];
};

// 从类型中选择指定的属性
type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

// 从类型中排除指定的属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// 使用示例
interface User {
    id: number;
    name: string;
    age: number;
}

type PartialUser = Partial<User>;           // { id?: number; name?: string; age?: number; }
type ReadonlyUser = Readonly<User>;         // { readonly id: number; readonly name: string; readonly age: number; }
type UserName = Pick<User, 'name'>;         // { name: string; }
type UserWithoutAge = Omit<User, 'age'>;    // { id: number; name: string; }

内置工具类型

Partial<T>

将所有属性变为可选:

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

type PartialUser = Partial<User>;
// 等价于 { id?: number; name?: string; }

Required<T>

将所有属性变为必需:

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

type RequiredUser = Required<User>;
// 等价于 { id: number; name: string; }

Readonly<T>

将所有属性变为只读:

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

type ReadonlyUser = Readonly<User>;
// 等价于 { readonly id: number; readonly name: string; }

Pick<T, K>

从类型中选择指定的属性:

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

type UserName = Pick<User, 'name'>;
// 等价于 { name: string; }

Omit<T, K>

从类型中排除指定的属性:

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

type UserWithoutAge = Omit<User, 'age'>;
// 等价于 { id: number; name: string; }

Record<K, T>

创建一个对象类型:

ts
type UserRecord = Record<string, number>;
// 等价于 { [key: string]: number; }

const users: Record<'admin' | 'user', { id: number; name: string }> = {
    admin: { id: 1, name: 'Admin' },
    user: { id: 2, name: 'User' }
};

Exclude<T, U>

从类型中排除指定的类型:

ts
type T0 = Exclude<'a' | 'b' | 'c', 'a'>;  // 'b' | 'c'
type T1 = Exclude<string | number, number>; // string

Extract<T, U>

从类型中提取指定的类型:

ts
type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'>;  // 'a'
type T1 = Extract<string | number, number>;      // number

NonNullable<T>

从类型中排除 null 和 undefined:

ts
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]

ReturnType<T>

获取函数返回值的类型:

ts
function fn() {
    return { x: 10, y: 20 };
}

type T = ReturnType<typeof fn>; // { x: number; y: number; }

Parameters<T>

获取函数参数的类型:

ts
function fn(a: number, b: string) {
    return a + b;
}

type T = Parameters<typeof fn>; // [number, string]

条件类型

基础语法

ts
// T extends U ? X : Y
type TypeName<T> = 
    T extends string ? "string" :
    T extends number ? "number" :
    T extends boolean ? "boolean" :
    T extends undefined ? "undefined" :
    T extends Function ? "function" :
    "object";

type T0 = TypeName<string>;  // "string"
type T1 = TypeName<number>;  // "number"
type T2 = TypeName<boolean>; // "boolean"

infer 关键字

infer 用于在条件类型中推断类型:

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

type T0 = ArrayElementType<string[]>;      // string
type T1 = ArrayElementType<number[]>;      // number
type T2 = ArrayElementType<(string | number)[]>; // string | number

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

// 获取函数参数的第一个参数类型
type FirstParameter<T> = T extends (arg: infer P, ...args: any[]) => any ? P : never;

分布式条件类型

当条件类型作用于联合类型时,会进行分布式计算:

ts
type ToArray<T> = T extends any ? T[] : never;

type StrArrOrNumArr = ToArray<string | number>; 
// 等价于 string[] | number[]
// 而不是 (string | number)[]

实际应用场景

1. API 响应类型

ts
interface ApiResponse<T> {
    code: number;
    message: string;
    data: T;
}

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

async function fetchUser(id: number): Promise<ApiResponse<User>> {
    const response = await fetch(`/api/user/${id}`);
    return response.json();
}

2. 缓存函数

ts
function memoize<T extends (...args: any[]) => any>(
    fn: T
): (...args: Parameters<T>) => ReturnType<T> {
    const cache = new Map();
    
    return (...args: Parameters<T>): ReturnType<T> => {
        const key = JSON.stringify(args);
        if (cache.has(key)) {
            return cache.get(key);
        }
        const result = fn(...args);
        cache.set(key, result);
        return result;
    };
}

3. 事件处理

ts
type EventHandler<T> = (event: T) => void;

interface MouseEvent {
    x: number;
    y: number;
}

interface KeyboardEvent {
    key: string;
}

class EventEmitter<T> {
    private handlers: EventHandler<T>[] = [];

    addHandler(handler: EventHandler<T>): void {
        this.handlers.push(handler);
    }

    emit(event: T): void {
        this.handlers.forEach(handler => handler(event));
    }
}

const mouseEmitter = new EventEmitter<MouseEvent>();
mouseEmitter.addHandler(event => console.log(`Mouse at (${event.x}, ${event.y})`));

4. 工厂模式

ts
interface Factory<T> {
    create(): T;
}

class UserFactory implements Factory<User> {
    create(): User {
        return { id: 1, name: 'Tom' };
    }
}

function createInstance<T>(factory: Factory<T>): T {
    return factory.create();
}

const user = createInstance(new UserFactory());

最佳实践

  1. 使用有意义的泛型参数名

    • T:单个类型
    • K:键类型
    • V:值类型
    • E:元素类型
  2. 使用约束提高类型安全

    ts
    function getLength<T extends { length: number }>(arg: T): number {
        return arg.length;
    }
  3. 合理使用默认类型

    ts
    interface Config<T = string> {
        value: T;
    }
  4. 避免过度使用泛型

    • 不是所有情况都需要泛型
    • 优先考虑代码的可读性
  5. 利用类型推断

    • 让 TypeScript 自动推断类型,而不是显式指定

最后更新:2025年

这是我的个人文档