是说在社群到处都在爆雷的《鱿鱼游戏》,我进度第一集,好想弃赛来追剧(我就废
然後谢谢没看鱿鱼游戏,不小心看到我文章的你~(还是你已经看完了没写很好,当作笔记记录,若有错误,欢迎留言指教,感恩的心。
泛型(Generics)是指在定义function、interfaces或class的时候,不预先指定具体的型别而在使用的时候再指定型别的一种特性。
在前面介绍 array 型别的时候,除了使用「型别 + 方括号」来表示 array, 也可以使用泛型方式来表示。 让我们复习一下:
//「型别 + 方括号」
const list1: number[] = [1, 2, 3];
//阵列泛型
const list2: Array<number> = [1, 2, 3];
这个例子来看,我传入参数的型别为number阵列型别,我们可以使用Array<number>
,也可以使用 number[]
, 返回值型别也是一样的数字阵列型别。 所以当传入[1, 2, 3]
时是没有问题的。
function identity(arg: Array<number>): Array<number> {
console.log(arg);
return arg;
}
identity([1, 2, 3]); // [1, 2, 3]
function identity2(arg: number[]): number[] {
console.log(arg);
return arg;
}
identity2([1, 2, 3]); // [1, 2, 3]
如果我们希望他不仅能处理数字阵列,也能处理字串阵列,那该怎麽做呢? >> 我们可以使用泛型,不预先指定具体的型别,在使用的时候再去指定或让 compiler 自动推论。
在函式後面加上 <Type>
表示动态型别,<Type>
命名也是可以自行定义的,如<List>
。只是 <T>
及 <Type>
比较通用。
再指定 arg 参数为 Type[]
型别, 函式也回传 Type[]
型别,那就能依据传入型别去回传该型别资料。当然你也可以只写Type
就可以了, 只是这个例子传入及输出型别都是阵列, 我们写Type[]
会更严谨,只传入阵列型别。像如果是字串写Type
就可以了。
function identity3<Type>(arg: Type[]): Type[] {
return arg;
}
let output1 = identity3([1, 2, 3]);
let output2 = identity3(["a", "b", "c"]);
console.log(output1);// [1, 2, 3]
console.log(output2); //["a","b","c"]
我们在调用函式的时候,可以将参数型别传给函式,如希望他是使用 <type>
如下方例子 identity3<string>(["a", "b", "c"])
,指定参数型别为 string
。 或是让 TypeScript 自动推论(type argument inference )出来,这个方法最常见,当然选这个呀,方便 XD
let output3 = identity3<string>(["a", "b", "c"]); //指定泛型回传型别 `<type>`
let output4 = identity3(["a", "b", "c"]); //inference
console.log(output3); // [ 'a', 'b', 'c' ]
console.log(output4); // [ 'a', 'b', 'c' ]
一样使用<T, U>
自定义,并对应到参数的型别就可以了。
function makeTuple<T, U>(tuple: [T, U]): [T, U] {
return [tuple[0], tuple[1]];
}
const tuple1 = makeTuple([1, "a"]);
console.log(tuple1); //[ 1, 'a' ]
顺便试看了 arrow function 写法 :
const makeTuple2 = <T, U>(tuple: [T, U]): [T, U] => {
return [tuple[0], tuple[1]];
}
const tuple2 = makeTuple2([1, "a"]);
console.log(tuple2); //[ 1, 'a' ]
⚠ arrow function 能在 .ts 但不能在 .tsx 中使用, 因为 tsx 中, TS compiler 会无法清楚区分 <> 是指 JSX 或 Generics Type, 所以当需要在函式中定义泛型时,就直接使用传统的 Function Statement。
在函式内部使用泛型变数的时候,由於事先不知道它是哪种型别,所以不能随意的操作它的属性或方法,如下方例子,泛型 T 不一定包含属性 length
,所以编译的时候报错了。
function loggingIdentity<T>(arg: T): T {
console.log(arg.length);
return arg;
}
//error:Property 'length' does not exist on type 'T'.
这时,我们可以对泛型进行约束,我们使用了 extends
约束了泛型 T 必须符合介面 Lengthwise
的形状,也就是必须包含 length
属性。下方例子, 如果我们带入的型别不符 Lengthwise
型别,就会报错提醒。
interface Lengthwise {
length: number;
}
function loggingIdentity2<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity2(3); //error: Argument of type 'number' is not assignable to parameter of type 'Lengthwise'.
loggingIdentity2({ length: 10, value: 3 });
可以指定受另一个型别约束的参数型别, 如 Key 收到 Type 的型别约束, 所以 key参数型别只能是 obj参数所定义的型别。
如下面例子 getProperty(x, "m")
时, 就会报错提醒没有 m 参数。
function getProperty<Type, Key extends keyof Type>(obj: Type, key: Key) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
let value1 = getProperty(x, "a");
let value2 = getProperty(x, "m"); //error: Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.
console.log(value1); //1
console.log(value2); //undefined
含有泛型的介面来定义函式的形状:
interface GenericIdentityFn {
<Type>(arg: Type): Type;
}
function identity<Type>(arg: Type): Type {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
可以把泛型引数提前到介面名上:
interface GenericIdentityFn<Type> {
(arg: Type): Type;
}
function identity<Type>(arg: Type): Type {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
⚠ 在使用泛型介面的时候,需要定义泛型的型别。
泛型也可以用於类别的型别定义中:
class GenericNumber<NumType> {
zeroValue: NumType;
add: (x: NumType, y: NumType) => NumType;
}
//限制为 number 型别
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
return x + y;
};
感谢阅读, 明天见!
https://willh.gitbook.io/typescript-tutorial/advanced/generics
https://www.typescriptlang.org/docs/handbook/2/generics.html
https://pjchender.dev/typescript/ts-generics#%E5%9F%BA%E6%9C%AC%E8%AA%9E%E6%B3%95
<<: 大共享时代系列_020_共同工作空间(Coworking Space)
>>: Learn How to Print Multiple Outlook Messages to PDF?
Nginx 直接写在 config 内 location / { allow 127.0.0.0/2...
今天再多来看看两个Vue的指令,v-cloak与v-pre v-cloak 使用v-cloak的原因...
前言 可选在上一篇中,提到一个概念就是暧昧。它是一种可以让程序介於「有值」、「没有值」的中间状态,这...
又是忙到下午4点才吃第一餐的一天~ 赶在下午4点半之前进场都算午餐价,费用是250元(不收服务费),...
前言 主要籍由这个主题,熟悉永丰金融API的相关操作。 将系列文章 做一下规划 环境建置 API串接...