范性 在使用的时候才确定 类型
🧩 一、泛型是什么?
👉 泛型是 类型的参数化(Type as parameter) 。
它允许我们在定义函数、接口或类时,不预先指定具体的类型,而是在使用时再传入类型。
例子:没有泛型的函数
sql
function identity(value: any): any {
return value;
}
问题是:
- 你传入 number,返回值类型却成了 any;
- TypeScript 无法推断返回值类型,类型安全丢失。
✅ 使用泛型改写
r
function identity<T>(value: T): T {
return value;
}
T是一个类型变量(type variable);- 当你调用时,TS 会根据传入值自动推断类型。
scss
identity(123); // 推断 T 为 number
identity("hello"); // 推断 T 为 string
返回值类型与参数类型保持一致,类型安全、灵活。
🧠 二、泛型的语法结构
基本结构是:
r
function fn<T>(arg: T): T {
return arg;
}
<T>:定义一个泛型变量;T可以是任意名字,比如<Type>,<U>,<K, V>;T在函数内部当作一种占位符使用。
🚀 三、泛型函数、接口、类型别名、类
1️⃣ 泛型函数
scss
function firstElement<T>(arr: T[]): T {
return arr[0];
}
firstElement([1, 2, 3]); // T 推断为 number
firstElement(["a", "b"]); // T 推断为 string
2️⃣ 泛型接口
csharp
interface Box<T> {
value: T;
}
const stringBox: Box<string> = { value: "hello" };
const numberBox: Box<number> = { value: 123 };
3️⃣ 泛型类型别名
typescript
type Pair<T, U> = [T, U];
const p1: Pair<string, number> = ["age", 18];
4️⃣ 泛型类
kotlin
class DataStore<T> {
private data: T[] = [];
add(item: T) {
this.data.push(item);
}
getAll(): T[] {
return this.data;
}
}
const stringStore = new DataStore<string>();
stringStore.add("hello");
stringStore.add("world");
🧮 四、泛型约束(Constraints)
有时你不希望 T 是任意类型,而是必须"具备某些特性"。
例子:想访问 .length 属性
r
function getLength<T>(arg: T): number {
return arg.length; // ❌ 报错:T 可能没有 length
}
解决办法:
scss
interface HasLength {
length: number;
}
function getLength<T extends HasLength>(arg: T): number {
return arg.length;
}
getLength("hello"); // ✅ string 有 length
getLength([1, 2, 3]); // ✅ 数组有 length
getLength(123); // ❌ number 没有 length
T extends U 表示:
泛型 T 必须是类型 U 的子类型(或满足其结构)。
🧩 五、泛型默认值
如果你想给泛型一个默认类型:
ini
interface ApiResponse<T = any> {
code: number;
data: T;
}
const res: ApiResponse = { code: 200, data: "ok" }; // 默认 T 为 any
🧩 六、泛型约束之间的依赖(类型参数之间的关系)
vbnet
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
const person = { name: "Alice", age: 18 };
getProperty(person, "name"); // ✅ OK
getProperty(person, "gender"); // ❌ Error:key 不存在于 T 中
解释:
K extends keyof T表示K必须是T的键名之一;- 这是 泛型之间的依赖约束,非常常见于框架源码。
🧩 七、泛型工具与推断技巧
1️⃣ 显式指定类型
c
identity<string>("hi");
2️⃣ 类型推断
TS 一般能自动推断类型,无需显式指定。
3️⃣ 泛型工具类型(TS 内置)
TypeScript 提供了许多内置泛型工具类型:
| 工具类型 | 作用 |
|---|---|
Partial<T> |
将所有属性变为可选 |
Required<T> |
将所有属性设为必需 |
Readonly<T> |
将所有属性设为只读 |
Pick<T, K> |
从类型 T 中挑出若干属性 |
Record<K, T> |
构造一个键为 K、值为 T 的对象类型 |
ReturnType<T> |
提取函数的返回值类型 |
这些都是通过泛型 + 映射类型 + 条件类型实现的。
⚙️ 八、真实案例:Axios 泛型返回值
ini
axios.get<User[]>('/api/users')
.then(res => {
res.data.forEach(u => console.log(u.name));
});
这里的 axios.get<T> 使用了泛型:
- 告诉 TS 响应体数据的类型;
- 让
res.data自动推断为User[]。