TypeScript 泛型
有些函数或容器的逻辑与具体类型无关(比如"取数组第一个元素"),但用 any 会丢掉类型检查。泛型(Generics)用类型变量 T 把"类型"变成可传递的参数:调用时传入什么类型,返回值的类型就跟着变,写一次、处处安全。
为什么要泛型
// 用 any:能跑但返回值类型丢失,调用方拿不到检查
function firstAny(arr: any[]): any {
return arr[0];
}
// 泛型:T 由调用时的参数推断
function first<T>(arr: T[]): T {
return arr[0];
}
const n = first([1, 2, 3]); // n 的类型是 number
const s = first(["a", "b"]); // s 的类型是 string
console.log(n, s); // 输出:1 a
泛型函数与多个类型参数
function toArray<T>(v: T): T[] {
return [v];
}
console.log(toArray(5)); // 输出:[5]
console.log(toArray("hi")); // 输出:['hi']
// 多个类型参数:<T, U> 按顺序对应
function pair<T, U>(a: T, b: U): [T, U] {
return [a, b];
}
console.log(pair(1, "x")); // 输出:[1, 'x']
用 extends 约束泛型
泛型不能随便调用方法,因为 T 未知。用 extends 限制"T 必须长成什么样":
interface HasLength {
length: number;
}
function logLen<T extends HasLength>(v: T): void {
console.log(v.length); // 有约束后可以放心访问 length
}
logLen("hello"); // 输出:5
logLen([1, 2, 3]); // 输出:3
// logLen(123); // 报错:number 没有 length 属性
泛型接口与泛型类
// 泛型接口:描述带类型的容器
interface Result<T> {
ok: boolean;
data: T;
}
const r: Result<string> = { ok: true, data: "成功" };
console.log(r.data); // 输出:成功
// 泛型类:存入与取出同一类型
class Box<T> {
constructor(private value: T) {}
get(): T {
return this.value;
}
}
const box = new Box<number>(42);
console.log(box.get()); // 输出:42
默认类型参数
不给类型参数时使用默认值,调用方少写一截:
function create<T = string>(): { value: T } {
return { value: "" as T };
}
const a = create(); // 未指定,T 采用默认 string
const b = create<number>(); // 显式指定 number
console.log(typeof a.value, typeof b.value); // 输出:string number
小结
泛型把"类型"本身变成参数:用 <T> 声明、用 extends 约束、可设默认值。它让数组、Promise、Map 等通用工具既复用逻辑又保留精确的类型,是读懂 TS 标准库类型定义的关键。