文章目录

TypeScript 泛型:<T> 类型参数与约束

发布于 2026-04-04 05:55:16 · 浏览 2 次 · 评论 0 条

TypeScript 泛型:<T> 类型参数与约束

TypeScript 的泛型机制让你能编写可重用、类型安全的代码,而无需提前锁定具体类型。核心在于 <T> 这种类型参数写法——它像一个“占位符”,在调用时才被替换成真实类型。


理解泛型的基本用法

  1. 定义 一个带泛型的函数,在函数名后加上 <T>

    function identity<T>(arg: T): T {
      return arg;
    }

    这里的 T 是类型变量,代表“任意类型”。函数接收一个 T 类型的参数,返回同样类型的值。

  2. 调用 该函数时,有两种方式指定 T 的实际类型:

    • 显式指定:identity<string>("hello")
    • 让 TypeScript 自动推断:identity("hello")(此时 T 被推断为 string
  3. 应用 泛型到接口或类,实现更灵活的数据结构:

    interface Box<T> {
      value: T;
    }
    
    const numberBox: Box<number> = { value: 42 };
    const stringBox: Box<string> = { value: "text" };

使用类型约束限制泛型范围

默认情况下,T 可以是任何类型。但有时你需要确保 T 具备某些属性或方法,这时就要用 extends 关键字添加约束。

  1. 定义 一个约束接口,规定必须包含的成员:

    interface Lengthwise {
      length: number;
    }
  2. 修改 泛型函数,要求 T 必须实现该接口:

    function logLength<T extends Lengthwise>(arg: T): T {
      console.log(arg.length); // 安全访问 length 属性
      return arg;
    }
  3. 调用 受约束的函数:

    • ✅ 合法:logLength("hello")(字符串有 length
    • ✅ 合法:logLength([1, 2, 3])(数组有 length
    • ❌ 报错:logLength(42)(数字没有 length,编译失败)

多个类型参数与复杂约束

当需要处理多个相关类型时,可以声明多个泛型参数,并对它们分别或联合施加约束。

  1. 声明 两个类型参数 KV,并约束 K 必须是对象键类型:

    function getProperty<K extends keyof T, T>(obj: T, key: K): T[K] {
      return obj[key];
    }

    这里 keyof T 表示 T 所有属性名的联合类型,确保传入的 key 是合法的属性名。

  2. 使用 该函数安全获取对象属性:

    const person = { name: "Alice", age: 30 };
    const name = getProperty(person, "name"); // 类型为 string
    const age = getProperty(person, "age");   // 类型为 number
    // getProperty(person, "height"); // 编译错误:height 不是 person 的属性
  3. 组合 多个约束条件,例如要求类型同时满足两个接口:

    interface A { a: number; }
    interface B { b: string; }
    
    function combine<T extends A & B>(obj: T): void {
      console.log(obj.a, obj.b); // 可安全访问 a 和 b
    }

泛型默认类型与实用技巧

你可以为泛型参数设置默认类型,简化常见场景的使用。

  1. 设置 默认类型,在 <T = DefaultType> 中指定:

    function createArray<T = string>(length: number, value: T): T[] {
      return Array(length).fill(value);
    }
  2. 调用 时省略类型参数,自动使用默认值:

    const strArr = createArray(3, "x");    // 类型为 string[]
    const numArr = createArray<number>(3, 1); // 显式覆盖默认类型,得到 number[]
  3. 在类中使用 泛型,默认类型让实例化更简洁:

    class Queue<T = number> {
      private items: T[] = [];
      enqueue(item: T) { this.items.push(item); }
      dequeue(): T | undefined { return this.items.shift(); }
    }
    
    const numQueue = new Queue();      // T 默认为 number
    const strQueue = new Queue<string>(); // 显式指定 T 为 string

避免常见错误

  1. 不要 在泛型函数内部假设类型的具体结构:

    // 错误示例:未加约束就访问属性
    function badExample<T>(arg: formulate T): void {
      console.log(arg.toString()); // 危险!虽然大多数类型有 toString,但不保证
    }

    正确做法是添加约束,或使用类型守卫。

  2. 不要 混淆类型参数和值参数:

    // 错误:试图把类型当作运行时值使用
    function wrong<T>(): void {
      console.log(T); // 编译错误!T 只存在于编译期
    }
  3. 谨慎使用 any 绕过泛型约束——这会破坏类型安全,失去泛型的意义。

// 不推荐:用 any 代替约束
function unsafe<T>(arg: any): T {
  return arg as T;
}

正确方式应通过合理约束确保类型安全。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文