泛型实用案例
泛型基础概念
泛型是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