T

1 阅读3分钟

1.T 是 泛型的“类型变量”(type parameter)。

T是什么:

  • T 不是值、不是变量、也不是关键字
  • 它代表“某一种类型”,具体是哪种类型,由你调用函数时传入的值来推出来(或你手动写出来)。

深深理解:

function wrap<T>(value: T): T

  • <T>:声明一个泛型类型变量 T
  • (value: T):这是接收参数,参数名叫 value,参数类型是 T
  • : T(括号后面那个):这是返回值类型,表示函数返回一个 T

所以:

  • 接收参数:value: T
  • 返回类型:最后的 : T

2.可以随便取名字:

类型变量(泛型参数)名字可以随便取,只要是合法标识符即可。

常见命名习惯(推荐)
  • 通用:T、U、V

  • 更语义化(更好读):TData、TItem、TResponse、TKey

例子:

function wrap<TData>(valueTData): TData {

  return value

}

注意 1 点

在同一个 <...> 作用域里,名字要一致才表示同一个类型:

  • <T>(a: T, b: T) 表示 a 和 b 同类型
  • <T, U>(a: T, b: U) 表示可以不同类型

例子:

function wrap<T>(value: T): T {
  return value
}

这里的意思是:

  • T 表示“value 的类型”
  • 你传什么类型进来,T 就是什么类型
  • 返回值类型也跟着是 T

调用时:

const a = wrap(123)   // 123 是 number,所以 T = number,a: number
const b = wrap('hi')  // 'hi' 是 string,所以 T = string,b: string

为什么要用 T

因为你想写一个通用函数:同一段代码既能处理 number,也能处理 string,还能保持“类型不丢失”。

T 这个字母可以换吗

可以,随便叫:

  • T(最常见:Type)
  • U、K、V
  • TItem、TData、TResponse(更语义化)

比如:

function wrap<TData>(value: TData): TData {
  return value
}

T 在“好几处地方”出现时,你可以用一个统一理解方式:

  • 同一个函数/类型定义里,T 是同一个“类型占位符”。它在不同位置出现,是在表达“这些位置的类型彼此有关联”。
  • 调用时确定一次 T 是什么类型,整个签名里所有 T 都一起跟着确定。

(1)最典型:参数是 T,返回也是 T

function wrap<T>(value: T): T {
  return value
}

const a = wrap(10)       // T 被推导成 number,所以 a: number
const b = wrap('hello')  // T 被推导成 string,所以 b: string

理解:

  • value 的类型 = T
  • 返回值类型 = T
  • 所以“传什么类型进来,就返回什么类型出来”

(2)多个参数共享同一个 T:要求它们类型一致

function first<T>(a: T, b: T): T {
  return a
}

first(1, 2)       // ✅ T = number
first('a', 'b')   // ✅ T = string
first(1, 'a')     // ❌ 报错:T 不可能同时是 number 和 string

理解:这里 T 出现在 a 和 b 上,表示 a 和 b 必须同类型。

(3)数组/容器里的 T:表示“元素类型”

function head<T>(list: T[]): T | undefined {
  return list[0]
}

head([1, 2, 3])       // T = number
head(['a', 'b'])      // T = string

理解:T[] 说明“这是个数组,数组元素类型是 T”。

疑问:数组里面的元素要一模一样吗?不一样可以吗?

(4)有多个类型变量:每个代表不同角色

function pair<T, U>(a: T, b: U): [T, U] {
  return [a, b]
}

pair(1, 'x') // [number, string]

理解:T 和 U 是两个独立的占位符,分别对应不同参数类型。

疑问:那意思就是说
pair(0,false) pair('111',false) pair(0,'123') pair(0,88)也可以吗?

5)带约束的 T extends ...:限制 T 的范围

function len<T extends { length: number }>(x: T) {
  return x.length
}

理解:T 仍然是占位符,但它必须“至少有 length:number”。

疑问:len(2.length)可以吗