阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > 泛型实用案例

泛型实用案例

作者:陈川 阅读数:47876人阅读 分类: TypeScript

泛型基础概念

泛型是TypeScript中强大的类型工具,它允许我们创建可重用的组件,这些组件可以支持多种类型而不丢失类型安全性。泛型通过在定义函数、接口或类时不预先指定具体类型,而在使用时再指定类型的方式工作。

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

let output1 = identity<string>("hello");  // 类型为string
let output2 = identity<number>(42);      // 类型为number

函数中的泛型应用

泛型函数是最常见的泛型使用场景。它们可以处理不同类型的数据而不需要重复编写相似的函数。

function reverse<T>(items: T[]): T[] {
    return items.reverse();
}

const numbers = [1, 2, 3];
const reversedNumbers = reverse(numbers);  // number[]

const strings = ["a", "b", "c"];
const reversedStrings = reverse(strings);  // string[]

接口与泛型结合

泛型接口可以定义灵活的数据结构,适用于多种数据类型。

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

let pair1: KeyValuePair<number, string> = { key: 1, value: "one" };
let pair2: KeyValuePair<string, boolean> = { key: "isActive", value: true };

类中的泛型实现

泛型类特别适合创建可重用的组件,如集合类。

class Queue<T> {
    private data: T[] = [];
    
    push(item: T) {
        this.data.push(item);
    }
    
    pop(): T | undefined {
        return this.data.shift();
    }
}

const numberQueue = new Queue<number>();
numberQueue.push(1);
numberQueue.push(2);

const stringQueue = new Queue<string>();
stringQueue.push("first");
stringQueue.push("second");

泛型约束

有时我们需要限制泛型的类型范围,这时可以使用泛型约束。

interface Lengthwise {
    length: number;
}

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

loggingIdentity("hello");  // 可以,字符串有length属性
loggingIdentity([1, 2, 3]);  // 可以,数组有length属性
loggingIdentity(3);  // 错误,数字没有length属性

泛型与默认类型

TypeScript允许为泛型参数指定默认类型,这在某些情况下非常有用。

interface PaginatedResponse<T = any> {
    data: T[];
    total: number;
    page: number;
}

const userResponse: PaginatedResponse<{ id: number; name: string }> = {
    data: [{ id: 1, name: "Alice" }],
    total: 1,
    page: 1
};

const defaultResponse: PaginatedResponse = {
    data: [1, 2, 3],  // 使用默认的any类型
    total: 3,
    page: 1
};

条件类型与泛型

TypeScript的条件类型允许基于条件表达式选择类型,与泛型结合可以创建强大的类型工具。

type NonNullable<T> = T extends null | undefined ? never : T;

type StringOrNumber = string | number | null;
type ValidType = NonNullable<StringOrNumber>;  // string | number

映射类型中的泛型

映射类型可以基于旧类型创建新类型,与泛型结合使用非常灵活。

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

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

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

type ReadonlyUser = Readonly<User>;
type PartialUser = Partial<User>;

实用工具类型示例

TypeScript内置了一些基于泛型的实用工具类型,可以简化开发。

// Pick: 从类型T中选择一组属性K
type UserPreview = Pick<User, "name">;

// Omit: 从类型T中排除一组属性K
type UserWithoutAge = Omit<User, "age">;

// Record: 构造一个类型,其属性键为K,属性值为T
type UserMap = Record<string, User>;

泛型在React组件中的应用

在React开发中,泛型可以用于创建类型安全的组件。

interface ListProps<T> {
    items: T[];
    renderItem: (item: T) => React.ReactNode;
}

function List<T>({ items, renderItem }: ListProps<T>) {
    return <div>{items.map(renderItem)}</div>;
}

// 使用示例
<List<number>
    items={[1, 2, 3]}
    renderItem={(item) => <div key={item}>{item}</div>}
/>

高级泛型模式

更复杂的泛型模式可以解决特定的类型问题。

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

// 获取构造函数类型
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;

// 元组转联合类型
type TupleToUnion<T extends any[]> = T[number];

type Example = TupleToUnion<[string, number, boolean]>;  // string | number | boolean

泛型与异步编程

泛型在处理异步操作时特别有用,可以确保类型在Promise链中正确传递。

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

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

async function getUser() {
    const user = await fetchData<UserData>("/api/user");
    console.log(user.name);  // 类型安全访问
}

泛型与高阶函数

泛型可以用于创建类型安全的高阶函数。

function memoize<T extends (...args: any[]) => any>(fn: T): T {
    const cache = new Map<string, ReturnType<T>>();
    return function(...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;
    } as T;
}

const add = (a: number, b: number) => a + b;
const memoizedAdd = memoize(add);

泛型与类型推断

TypeScript的类型推断系统可以自动推断泛型参数,减少冗余代码。

function merge<T, U>(obj1: T, obj2: U): T & U {
    return { ...obj1, ...obj2 };
}

// 不需要显式指定类型参数
const result = merge({ name: "Alice" }, { age: 30 });
// result的类型自动推断为 { name: string } & { age: number }

泛型与函数重载

泛型可以与函数重载结合,提供更精确的类型定义。

function process<T extends string | number>(input: T): T extends string ? number : string;

function process(input: any): any {
    return typeof input === "string" ? input.length : input.toString();
}

const strResult = process("hello");  // number
const numResult = process(42);      // string

泛型与索引类型

索引类型查询和索引访问操作符可以与泛型结合使用。

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

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

泛型与类型守卫

自定义类型守卫可以与泛型结合,提供更安全的类型检查。

function isArrayOf<T>(arr: any[], typeGuard: (item: any) => item is T): arr is T[] {
    return arr.every(typeGuard);
}

function isString(item: any): item is string {
    return typeof item === "string";
}

const data: unknown = ["a", "b", "c"];
if (isArrayOf(data, isString)) {
    // 在此块中,data被推断为string[]
    data.forEach(s => console.log(s.toUpperCase()));
}

泛型与可变元组类型

TypeScript 4.0引入的可变元组类型可以与泛型结合,创建更灵活的函数签名。

function concat<T extends unknown[], U extends unknown[]>(
    arr1: [...T],
    arr2: [...U]
): [...T, ...U] {
    return [...arr1, ...arr2];
}

const result = concat([1, 2], ["a", "b"]);
// result的类型为 [number, number, string, string]

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌