理解 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']))
如果你像我一样,你可能会问:
- 这里的 T 到底是什么?
- 为什么要使用 T,这是任意的吗?
- 为什么我不能只写 loggingIdentity(arg: Lengthwise)?
- 是什么意思?
什么是<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])
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)
}
显然我们可以做得更好,除了泛型之外还有其他方法可以使用吗?
联合类型与泛型
如果你考虑过联合类型,那真是个好主意。但它也有一些限制!
假设我们想使用接受字符串 | 数字联合类型作为其参数的函数的返回值。
// 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'.
联合类型限制了函数的返回类型。
使用泛型,我们可以明确地告诉 TypeScript myVal 是一个数字,而不是一个字符串或一个数字!
function returnSomething<T>(arg: T): T {
return arg
}
const myVal = returnSomething(123)
const myOtherVal = returnSomething('hello')
myVal + 1 // 👍👍 All good!
重载
好的,那么您可能会问函数重载怎么样?
看看下面的代码。当然,这个也能用,但我还是把具体实现方式留给你自己决定吧。
// 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
<T 扩展...
太棒了,我觉得你开始明白了。那就让我们一起来揭开谜底吧。
泛型也并非完美。我们需要通过添加一些约束来理解它们的“约束” ;)
function getLength<T>(args: T) : number {
return args.length;
}
上述函数将导致 TypeScript 抱怨,因为我们需要告诉 TypeScript T 扩展了适当的类型并且可以安全地调用.length
!
interface ThingWithLength {
length: number
}
function getLength<T extends ThingWithLength>(args: T) : number {
return args.length; // 😅 All good now!
}
未来阅读
感谢你的关注!如果你喜欢,请访问https://codeamigo.dev获取交互式教程!
文章来源:https://dev.to/plondon/understanding-generics-in-typescript-2hfh