注意: 如果您是 TypeScript 新手,请先查看我们的 TypeScript 入门 教程。
泛型是 TypeScript 的一项功能,它允许你编写能够处理任何数据类型,但仍然保留类型安全的函数、类或接口。
你不需要使用像 string
或 number
这样的固定类型,而是使用一个占位符(如 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
属性(如string、array 等)。
现在,只有具有 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 或实用函数,其中输入/输出类型可能会发生变化。
另请阅读