「泛型(Generics)」是 TypeScript 中很常会使用到的功能,泛型的概念简单来说,就是让「型别」也变成一个变数,可以根据不同的情况套用不同的型别。因为在强型别的语言中,不论变数或函式的回传值的型别,都会需要被加以定义。
举例来说,如果我们写一个能够回传阵列第一个元素的函式,我们会需要这样写:
function getFirstElementOfNumberArray(arr: number[]): number {
const [firstElement] = arr;
return firstElement;
}
你会发现,我们会需要定义这个函式所接受的参数型别,例如这里是 number[]
,且函式回传值的型别因为是阵列中的第一个元素,所以也会是 number
。
这时候我们可以这样用:
const firstElement = getFirstElementOfNumberArray([1, 9, 8, 8]); // 1
但你会发现因为型别定义的关系,这个函式只能接受 number[]
作为它的参数,如果你是想要取出 string[]
的话,因为型别合,TS 会报错,像是这样:
const firstElement = getFirstElementOfNumberArray([
'Oh.So.Pro.',
'就。很。Pro',
'非常。Pro',
]);
你原本可能会预期它应该要取出阵列中的第一个元素给你,也就是「Oh.So.Pro.」,结果 TS 在编译时却发生错误:
这个错误并不是因为我们在程序中使用了中文字,而是因为一开始我们定义 getFirstElement
这个函式,只能接受型别为 number[]
的参数,但现在我们却带入了型别为 string[]
的参数。
如果没有泛型的话,我们变成需要针对 string[]
的参数多写一个函式,取名为 getFirstElementOfStringArray
像是这样:
// 定义一个能够接受 string[] 作为参数的函式
function getFirstElementOfStringArray(arr: string[]): string {
const [firstElement] = arr;
return firstElement;
}
如此就能正确呼叫这个函式:
const firstElement = getFirstElementOfStringArray([
'Oh. So. Pro.',
'就很 Pro',
'非常 Pro',
]);
console.log(firstElement); // "Oh. So. Pro."
这时候你会发现,在不管型别的情况下,getFirstElementOfNumberArray
和 getFirstElementOfNumberArray
这两个函式明明就内容是完全相同的,但却因为型别的限制,逼着我们要拆成两个不同的函式。
好在,在 TypeScript 中有泛型可以使用,前面有提到,泛型的概念其实就像是把型别也变成一个变数,回到上面这两个 function 来看,你会发现这两个函式完全只差在「型别」是不同的(下图粉色框框):
因此这时候我们可以做一个抽象化的动作,把这个型别也变成一个变数,这里取名叫 T
,就可以把这两个只有型别不同的函式整合成一个,变成这样:
function getFirstElement<T>(arr: T[]): T {
const [firstElement] = arr;
return firstElement;
}
如此在使用 getFirstElement
时,你可以把型别当成变数一样,放入 <T>
的 T 中,明确告诉 TypeScript 这时候的函式参数型别是什麽,像是这样:
有了泛型的好处是,你将不在需要只因为型别的不同而要才成多个不同的函式。
更重要的是,一般来说,我们不需要明确在 <>
中告诉 TS 使用的参数型别是什麽,而是可以让 TS 自己推导(称作:type argument inference),因此,我们更常直接这样写:
// TS 会自动根据带入的参数推导这里的 T 是 number
const firstElement1 = getFirstElement([1, 9, 8, 8]);
// TS 会自动根据带入的参数推导这里的 T 是 string
const firstElement2 = getFirstElement([
'Oh.So.Pro.',
'就。很。Pro',
'非常。Pro',
]);
当我们把滑鼠移到函式上方时,可以看到 TS 自动推导的结果:
除了在函式中可以使用泛型外,在 Type Alias 中也可以使用泛型的概念。
假设一般来说 age
的型别是 number
:
type Person = {
age: number;
};
const john: Person = {
age: 20,
};
但某种形况下,age 会用中文字来表示,例如「二十
」,这时候你会发现再次发生型别的错误,因为我们试图把 string 带入型别为 number 的栏位中:
这时候,如果没有泛型的话,将只能像这样建立两个不同的型别:
type PersonWithNumberAge = {
age: number;
};
type PersonWithStringAge = {
age: string;
};
但有了泛型的话,更好的方式就是使用泛型,像这样:
type Person<T> = {
age: T;
};
使用时就可以这样:
const john1: Person<number> = {
age: 30,
};
const john2: Person<string> = {
age: '二十',
};
由於 type alias 并不像函式一样有 type argument inference,因此需要明确把型别定义在
<>
中,如上面的<number>
和<string>
。
透过泛型,能够把型别当成变数使用,是不是非常方便啊!
https://tsplay.dev/m3z43N @ TypeScript Playground
>>: day17 : kafka服务应用 on K8S (上)
$ curl https://sh.rustup.rs -sSf | sh -- -y 需要 GCC...
[Day28] 说明:google提供的帐号API 一、设定google console的oauth...
阿罗哈~ 现在心情之放松 原因是游戏专题告一段落拉 排名会在下周公布 保佑我们可以在中间以上的名次就...
当AI有了常识... 深度学习领域的巨擘,同时也是Facebook的AI研究院长杨立昆(Yann L...
根据ISO 31000,风险是“不确定性对目标的影响(effect of uncertainty o...