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 标准库类型定义的关键。

笔记加载中…