TypeScript 泛型

注意: 如果您是 TypeScript 新手,请先查看我们的 TypeScript 入门 教程。


泛型是 TypeScript 的一项功能,它允许你编写能够处理任何数据类型,但仍然保留类型安全的函数接口

你不需要使用像 stringnumber 这样的固定类型,而是使用一个占位符(如 T),该占位符在编译时会被实际类型替换。

例如,

// Generic function that returns the input value
function identity<T>(value: T): T {
  return value;
}

console.log(identity<string>("Hello"));
console.log(identity<number>(123));

输出

Hello
123

identity<T> 函数使用泛型类型 T,这意味着它可以接受任何类型的输入并返回相同类型的输出。

  • identity<string>("Hello")T 设置为 string,因此该函数接受并返回一个字符串。
  • identity<number>(123)T 设置为 number,因此它返回一个数字。

泛型接口

泛型接口允许你定义一种数据结构,它可以与不同数据类型协同工作,同时保持类型安全。

与泛型函数类似,它们使用类型占位符(如 T),该占位符可以在使用接口时设置。例如:

// Define a generic interface Box
interface Box<T> {
  value: T; // The value can be of any type specified when using the interface
}

// Create a Box of type string
let stringBox: Box<string>;
stringBox = { value: "Hello" }; // value must be a string
console.log(stringBox.value);  

// Create a Box of type number
let numberBox: Box<number>;
numberBox = { value: 42 }; // value must be a number
console.log(numberBox.value); 

输出

Hello
42

这里,

  • Box<T> 是一个泛型接口,其中 T 可以是任何类型。
  • Box<string> 表示该值必须是 string
  • Box<number> 表示该值必须是 number

泛型类

泛型类允许你定义一个可以处理任何数据类型,同时仍然保持类型安全的类。

你使用一个类型参数(如 T),在实际使用该类时,它会被替换为特定的类型。

class Container<T> {
  private data: T;

  constructor(value: T) {
    this.data = value;
  }

  getData(): T {
    return this.data;
  }
}

// Create a container for a string
const stringContainer = new Container<string>("Programiz");
console.log(stringContainer.getData()); 

// Create a container for a number
const numberContainer = new Container<number>(123);
console.log(numberContainer.getData());

输出

Programiz
123

这里,

  • T 是一个泛型类型,用于定义该类将存储的数据类型。
  • 当你创建一个新对象(new Container<string>("Programiz"))时,T 会变成 string,因此所有属性和方法都将期望并返回字符串。

同样,同一个类也可以用于任何数据类型,而无需为每种类型重写它。


泛型约束

泛型约束允许你限制可以使用泛型的类型。

你通过使用 extends 关键字来实现这一点,它确保传入的类型必须具有某些属性或遵循特定的结构。

例如,

function logLength<T extends { length: number }>(item: T): void {
  console.log(item.length);
}

logLength("Hello");       // Works: string has a length
logLength([1, 2, 3]);      // Works: array has a length
// logLength(123);         // Error: number doesn't have length

输出

5
3

T extends {length: number } 意味着:类型 T 必须有一个 length 属性(如stringarray 等)。

现在,只有具有 length 属性的值(如字符串或数组)才能传递给该函数。

注意:如果没有约束,泛型将接受任何类型 — 如果需要,约束可以帮助缩小范围。


将泛型与数组一起使用

你可以使用泛型使函数能够处理任何类型的数组,同时保持类型安全。

function getFirstElement<T>(arr: T[]): T {
  return arr[0];
}

console.log(getFirstElement<string>(["a", "b", "c"])); // Output: a
console.log(getFirstElement<number>([10, 20, 30]));    // Output: 10

在这里,T[] 表示一个类型为 T 的数组。

函数 getFirstElement<T>(arr: T[]): T 是泛型的,因此它可以处理任何类型的数组 — 如 string[]number[]boolean[],甚至自定义对象数组 — 同时仍然保持类型安全。


常见问题

默认泛型类型

有时,你可以为泛型提供一个默认类型,如果没有传递类型,则会使用该默认类型。例如:

interface ApiResponse<T = string> {
  data: T;
  success: boolean;
}

const response1: ApiResponse = { data: "OK", success: true };      // Uses default: string
const response2: ApiResponse<number> = { data: 200, success: true }; // Uses number

何时使用泛型

当以下情况时使用泛型:

  • 你想让你的代码能够处理多种数据类型。
  • 你想避免为不同类型重复相同的逻辑。
  • 你想维护类型安全,以便 TypeScript 可以在编译时捕获错误。

示例

  • 创建可重用的函数,如 identity<T>(value: T): T,它可以处理任何类型。
  • 构建灵活的数据结构,如 Box<T>Response<T>
  • 编写 API 或实用函数,其中输入/输出类型可能会发生变化。

另请阅读

你觉得这篇文章有帮助吗?

我们的高级学习平台,凭借十多年的经验和数千条反馈创建。

以前所未有的方式学习和提高您的编程技能。

试用 Programiz PRO
  • 交互式课程
  • 证书
  • AI 帮助
  • 2000+ 挑战