泛型
泛型(Generics
)是 TypeScript
的一种强大特性,允许我们在定义函数、类、接口或类型时,不预先指定具体的类型,而是在使用时再传入具体的类型。这样使得代码更加灵活、可重用,同时保持类型安全。
🧩 1. 为什么需要泛型?
在 JavaScript
中,函数和类通常是用来处理多种类型的数据的。为了支持多种类型,通常会使用 any
类型,但这会丢失类型检查的能力:
function identity(arg: any): any {
return arg;
}
const result = identity(123); // result 的类型是 any
使用 any
虽然灵活,但无法保证类型安全。我们希望函数能接受任意类型的参数,但返回值类型和参数类型保持一致,这就是泛型的作用。
🧩 2. 泛型的基本语法
定义一个支持泛型的函数:
function identity<T>(arg: T): T {
return arg;
}
🔹 解释:
T
是一个泛型类型变量,表示任意类型。identity
函数接受一个类型为T
的参数,并返回类型为T
的值。调用时指定具体类型:
const result1 = identity<number>(123); // result1 的类型是 number
const result2 = identity<string>("Hello"); // result2 的类型是 string
也可以自动推断类型:
const result3 = identity(123); // TypeScript 自动推断 T 为 number
🧩 3. 泛型函数
🔹 定义泛型函数:
function getArray<T>(items: T[]): T[] {
return items;
}
传入一个数组,并返回一个相同类型的数组。
const numberArray = getArray<number>([1, 2, 3]); // number[]
const stringArray = getArray<string>(["a", "b", "c"]); // string[]
🧩 4. 泛型接口
🔹 定义泛型接口:
interface GenericIdentityFn<T> {
(arg: T): T;
}
const identity: GenericIdentityFn<number> = (arg) => arg;
console.log(identity(123)); // 输出:123
🧩 5. 泛型类
🔹 定义泛型类:
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
const myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = (x, y) => x + y;
console.log(myGenericNumber.add(10, 20)); // 输出:30
🧩 6. 多个类型参数
泛型可以同时接受多个类型参数:
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
const result = swap<number, string>([123, "Hello"]);
console.log(result); // 输出:["Hello", 123]
🧩 7. 泛型约束(Constraints
)
Constraints
)有时候我们希望限制泛型只能是某些类型的子类型。这时可以使用泛型约束:
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("Hello"); // 有 length 属性
logLength([1, 2, 3]); // 有 length 属性
// logLength(123); // ❌ 报错:number 类型没有 length 属性
🔹 解释:
• T extends Lengthwise
表示 T
必须具有 Lengthwise
接口的属性(即必须有 length
属性)。
🧩 8. 泛型与类型推断
TypeScript
通常可以自动推断泛型类型,因此在很多情况下,我们不需要手动指定类型:
function identity<T>(arg: T): T {
return arg;
}
const result = identity(123); // 自动推断 T 为 number
🧩 9. 泛型工具类型(Utility Types
)
Utility Types
)TypeScript
内置了许多基于泛型的工具类型,常见的有:
Partial<T>
将所有属性变为可选
Required<T>
将所有属性变为必选
Readonly<T>
将所有属性变为只读
Pick<T, K>
从类型 T 中选取部分属性
Omit<T, K>
从类型 T 中排除指定的属性
Record<K, T>
构建一个键值对类型
ReturnType<T>
获取函数的返回值类型
🔹 示例:
interface Person {
name: string;
age: number;
}
type PartialPerson = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
🧩 10. 泛型默认类型
你可以为泛型提供默认类型:
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
const stringArray = createArray(3, "Hello"); // T 默认为 string
const numberArray = createArray<number>(3, 42); // 显式指定 T 为 number
🧩 11. 泛型工具函数
TypeScript
的工具函数常用泛型来处理复杂的类型转换。以下是一个工具函数的示例:
function merge<T, U>(obj1: T, obj2: U): T & U {
return { ...obj1, ...obj2 };
}
const person = merge({ name: "Alice" }, { age: 25 });
console.log(person); // 输出:{ name: "Alice", age: 25 }
🧩 12. 实战案例
用泛型实现一个简单的缓存工具类:
class Cache<T> {
private data: Map<string, T> = new Map();
set(key: string, value: T): void {
this.data.set(key, value);
}
get(key: string): T | undefined {
return this.data.get(key);
}
}
const cache = new Cache<number>();
cache.set("age", 25);
console.log(cache.get("age")); // 输出:25
🧩 13. 总结
类型安全
泛型保留了类型信息,避免了 any
类型的缺点。
灵活性
支持多种类型,避免重复编写相同逻辑的代码。
可读性强
泛型使代码结构清晰、易于维护。
👨💻 泛型使 TypeScript
在保持类型安全的同时,也能编写高度灵活和可复用的代码!
最后更新于
这有帮助吗?