理解 TypeScript 中的泛型

2025-06-04

理解 TypeScript 中的泛型

温馨提示!如果您想以交互方式体验这篇文章,请访问https://codeamigo.dev/lessons/151

介绍

有时,当我学习一个新的范式时,看似最简单的东西却会让我绊倒。我经常会忽略某些概念,因为它们一开始看起来很棘手。

TypeScript 泛型就是其中一个概念。

我们来看下面的例子:

interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

console.log(loggingIdentity(['hello world']))
Enter fullscreen mode Exit fullscreen mode

如果你像我一样,你可能会问:

  1. 这里的 T 到底是什么?
  2. 为什么要使用 T,这是任意的吗?
  3. 为什么我不能只写 loggingIdentity(arg: Lengthwise)?
  4. 是什么意思?

什么是<T>

<T>. T 告诉 TypeScript 这个类型将在运行时声明,而不是编译时。它是 TypeScript 的泛型声明

interface Lengthwise {
  length: number;
}

function logSomething<T>(arg: T): T {
  console.log(arg);
  return arg;
}

logSomething<string>('hello world')
logSomething<Array<number>>([1])
Enter fullscreen mode Exit fullscreen mode

logSomething<string>告诉 TypeScript:你接收的参数将是一个字符串,并且函数的返回类型也将是一个字符串。

为何<T>使用?

无论您使用<T><U><V>还是<Type>,都是任意的。

我们看到 alot 的用法是因为原始 TypeScript 文档就是这样定义的。不过,文档现在已经用 with 替换了之前的声明。所以,一切由你决定 :)

泛型有何用处?

此时您可能会想,“我为什么还要使用泛型?”

好吧,假设您想要一个类似于 logSomething 的类型安全的日志函数,用于数字和字符串。

function logString(arg: string) {
  console.log(arg);
}

function logNumber(arg: number) {
  console.log(arg)
}
Enter fullscreen mode Exit fullscreen mode

显然我们可以做得更好,除了泛型之外还有其他方法可以使用吗?

联合类型与泛型

如果你考虑过联合类型,那真是个好主意。但它也有一些限制!

假设我们想使用接受字符串 | 数字联合类型作为其参数的函数的返回值。

// function logString(arg: string) {
//   console.log(arg);
// }

// function logNumber(arg: number) {
//   console.log(arg)
// }

function returnStringOrNumber(arg: string | number) {
  return arg
}

const myVal = returnStringOrNumber(123)
const myOtherVal = returnStringOrNumber('hello')

myVal + 1 // <= Operator '+' cannot be applied to types 'string | number' and 'number'.
Enter fullscreen mode Exit fullscreen mode

联合类型限制了函数的返回类型。

使用泛型,我们可以明确地告诉 TypeScript myVal 是一个数字,而不是一个字符串或一个数字!

function returnSomething<T>(arg: T): T {
  return arg
}

const myVal = returnSomething(123)
const myOtherVal = returnSomething('hello')

myVal + 1 // 👍👍 All good!
Enter fullscreen mode Exit fullscreen mode

重载

好的,那么您可能会问函数重载怎么样?

看看下面的代码。当然,这个也能用,但我还是把具体实现方式留给你自己决定吧。

// GENERICS
// function returnSomething<T>(arg: T): T {
//   return arg
// }

// OVERLOADING
function returnSomething(arg: number): number;
function returnSomething(arg: string): string
function returnSomething(arg: number | string) { return arg }

const myVal = returnSomething(123)
const myOtherVal = returnSomething('hello')

myVal + 1
Enter fullscreen mode Exit fullscreen mode

<T 扩展...

太棒了,我觉得你开始明白了。那就让我们一起来揭开谜底吧。

泛型也并非完美。我们需要通过添加一些约束来理解它们的“约束” ;)

function getLength<T>(args: T) : number {
  return args.length;
}
Enter fullscreen mode Exit fullscreen mode

上述函数将导致 TypeScript 抱怨,因为我们需要告诉 TypeScript T 扩展了适当的类型并且可以安全地调用.length

interface ThingWithLength {
  length: number
}

function getLength<T extends ThingWithLength>(args: T) : number {
  return args.length; // 😅 All good now!
}
Enter fullscreen mode Exit fullscreen mode

未来阅读

感谢你的关注!如果你喜欢,请访问https://codeamigo.dev获取交互式教程!

文章来源:https://dev.to/plondon/understanding-generics-in-typescript-2hfh
PREV
如何减小 Docker 镜像大小:DevOps 工程师的最佳实践和技巧
NEXT
面试时你应该问的问题