泛型允许我们在定义函数、接口或类时不预先指定具体类型,而是在使用时再指定。这种类型参数化的机制让代码更加灵活和可复用。
泛型的核心概念是类型参数,使用 <T> 语法定义。T 是一个占位符,表示某种类型。
function identity<T>(arg: T): T {
return arg
}
const str = identity<string>("hello")
const num = identity(123)
console.log(typeof str)
console.log(typeof num)
<T> 声明了一个类型参数 T,函数的参数和返回值都使用这个类型。调用时可以显式指定类型,也可以让 TypeScript 自动推断。
可以定义多个类型参数,用逗号分隔。
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value]
}
const p1 = pair("name", "张三")
const p2 = pair(1, true)
console.log(p1)
console.log(p2)
pair 函数有两个类型参数 K 和 V,分别表示键和值的类型。返回值是一个元组,包含这两种类型。
类型参数可以使用任意名称,但有一些常见的约定:
T:Type,最常用的泛型参数名K:Key,表示键类型V:Value,表示值类型E:Element,表示元素类型R:Return,表示返回值类型interface Map<K, V> {
get(key: K): V | undefined
set(key: K, value: V): void
}
class MyMap<K, V> implements Map<K, V> {
private data: Record<string, V> = {}
get(key: K): V | undefined {
return this.data[String(key)]
}
set(key: K, value: V): void {
this.data[String(key)] = value
}
}
const map = new MyMap<string, number>()
map.set("one", 1)
map.set("two", 2)
console.log(map.get("one"))
使用有意义的名称可以让代码更加清晰。
TypeScript 可以根据传入的参数自动推断类型参数。
function first<T>(arr: T[]): T | undefined {
return arr[0]
}
const n = first([1, 2, 3])
const s = first(["a", "b", "c"])
console.log(typeof n)
console.log(typeof s)
调用 first 时没有显式指定类型,TypeScript 根据数组元素的类型推断出 T。
有时需要显式指定类型参数,特别是当 TypeScript 无法正确推断时。
function parseJSON<T>(json: string): T {
return JSON.parse(json)
}
interface User {
id: number
name: string
}
const user = parseJSON<User>('{"id": 1, "name": "张三"}')
console.log(user.name)
parseJSON 函数返回值的类型需要显式指定,因为 TypeScript 无法从 JSON 字符串推断出对象类型。
类型参数在声明它的范围内有效。
function wrap<T>(value: T): { value: T } {
return { value }
}
const wrapped = wrap("hello")
console.log(wrapped.value)
class Container<T> {
private value: T
constructor(value: T) {
this.value = value
}
getValue(): T {
return this.value
}
}
const container = new Container(123)
console.log(container.getValue())
函数的类型参数在函数内部有效,类的类型参数在整个类中有效。
类型参数与联合类型是不同的概念。
function process1<T extends string | number>(value: T): T {
return value
}
function process2(value: string | number): string | number {
return value
}
const r1 = process1("hello")
const r2 = process2("hello")
console.log(typeof r1)
console.log(typeof r2)
使用泛型时,返回值类型与参数类型相同。使用联合类型时,返回值是联合类型。泛型保留了更精确的类型信息。
类型参数可以有约束,限制可以接受的类型范围。
interface Lengthwise {
length: number
}
function logLength<T extends Lengthwise>(arg: T): number {
return arg.length
}
console.log(logLength("hello"))
console.log(logLength([1, 2, 3]))
console.log(logLength({ length: 10 }))
T extends Lengthwise 表示 T 必须满足 Lengthwise 接口的要求,即必须有 length 属性。
泛型基础包括类型参数的定义和使用。类型参数是类型的占位符,在使用时确定具体类型。可以定义多个类型参数,TypeScript 可以自动推断或显式指定类型。类型参数可以有约束,限制可接受的类型范围。理解泛型基础是掌握 TypeScript 高级特性的关键。