类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

本文由图雀社区成员 pftom 写作而成,欢迎参与图雀社区,一同创作精彩的免费技能教程,予力编程工作开展。

欢迎阅读 类型即正义,TypeScript 从入门到实践系列:

  • 《类型即正义:Ty: – 7 * w d f 7 –peScript 从入门! B N : ` A ? = o到实践(序章H j X d :)》
  • 《类型即正义o : F o W:TypeScript 从入门到实践(一)》
  • 《类型即正义:TypeScript 从入门到实践(二)》
  • 《类6 e E型即正义:TypeScript 从入门到实践(三)》
  • 《类型即正义:TypeScript 从入门到实践(四)》(也就是s 1 & Y ) R ^ {这篇)

本文所触及的源代码都放在了 Github 或[ J 4 D 8 ! K !者 Gitee 上,如果您觉得我们写得还不错,期望您能给❤️这篇文章点赞+GithubGitee库房加星❤️哦~

此教程属于 React 前端工程师学习道路的一部分,欢迎来 Star 一波,鼓动我们继续创作出更好的教程,继续更新中~

在之前的文章中,我们了解了 TypeScript 主要分为 JS 语言侧和类型侧两个部分。

在介m , % e f x ] 3 4绍了类型侧的一些根底知识,我们用这些学到的根底知识去注解对h 3 2应的 JS 内容,将 JS 内容如变量、函数、类等g ( / J ^类型化,c K !这样确保写出的C D / g (代码十分利于团队协作,且能快速排错。

在了解了之前几篇文章里面的知识之后,你应该可以运用 TypeScript 进行正常的项目开发了。

源起

为什么要学泛型?因为它可以帮助你 “面向编辑器代码提示编程” :)

学习准备

  • VSCode 编辑器,你可以拜访这个网站来下载code.visualstudio.com/
  • 设备 Node 环境,本系列教程榜首篇现已解t o w /

配备 TypeScript 环境

创建一个 node 项目:

mkdir ts-study
cd! ( P Y F } ts-study && npm init -y

配备 TypeSd i + S V y ]cript 环境:

npm install typescript # 设备 TypeScript
npx tsc --init # 生成 TypeScript 配备文件

批改 tsconfig.json文件,& u k设置对应的 TS 编译器需求编译的文件如下:

{
"compilerOptio% O h % k R d FnsX M N *": {
"outDir": "./dist" // 设置编译输出的文件夹
},
"include": [
// 需求编译的ts文件一个*标明2 y Z文件匹配**标明忽略文件的深度问题
"./src/**/*.ts"
],
"exclude": ["node_modules", "dist", "**/*.te a @ `st.ts"] // 扫除不需求编译的文件夹
}

配备 TypeScript 编译履行脚本,运用 VSCode 编辑器打开 ts-studyS ; : O H项目,然后批改 package.jsonscripts字段如_ G L G 1 下:

{
"nb 8 u S q R 6 t 6am[ B `e": "$ t ) Q T Tts-study",
"version": "1.0.0",
G q . u e"descre # . - ` 6 *iption": "",
"main": "index.js",
"9 . M X 6 c ,sc# b $ripts": {
"te* ( ) _ c Wst": "echo "Error: no test specified" && exit 1",
"build": "tsc",
"build:w": "tsc -w% m 7 7 a E "
},
"author": "pftom <1043269994@qq.com>",
"license": "MIT",
"depenU P T hdencies": {
"typescript": "^3.7.4"
}
}

接着在项目根目录新建 src文件夹,并在里面新建 ind, X [ Qex.ts 文件,接着在项目根目录下的命令行履行 npm run build:w初步监g W ^ 5 cindex.ts文件的批改。

通过上面的操作,你的 VSCode 编辑器应该是如下姿势:

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

其间 TERMINAL 终端标明h . Z G f 5 ; &正在监听文件批改并编译中,当时文件的编译结果没有过错,因为我们的 src/index.ts里面还没有写任何内容。全部准备就绪,初步 “面向编辑器代码提示编程”!✌️

从一个简略的比如说起

先不扯那么多泛型的概念,我们先来看一个简略的比如,F j Y G ? s 2src/index.ts里面编写如下代码:

function getTutureTutorialsInfo(info) {
return info;
}

我们编写了一个获取图雀教程信息的函数,接收 iK p z o D a k Qnfo输入,然后回来 ino 3 Ofo,即明晰参数类型和回来类型相同。现在这个还只是一个 Javb 2 ; ,aScript 函数,我们来给它进行类型注解。

写一个 Low 一点的 TS 函数

…. 这怎样注解?此时正在阅读的你可能会冒出这个疑问。

对的,这怎样注解?我们面临着如下几个难题:

  • info类型不知道,它可能是 stringnumber或许其他类型
  • info类型不知道的状况下,我们还要注解回来值类型,并且此回来o ? y )值类型Y ] q L ?要和 info类型一同,所以我们的回来值类型这儿也不知道。

相信有同学会检验这样去处理:

function getTuture/ O ) l K 7 + CTutorialsInfo(info: any): any {
return info;
}

很好!你成功写了榜首个 “AnyScript` 函数,这个函数和 JS 函数无异,底子无法用到 TS 的强3 x ^ M 9 K壮类型补全机制,不信你可以在你的 VSCode 编辑器里面测H i f ]验参与如下代码:

function getTutureTutorialsInfo(info: any)Q ) J % ) ] r: any {
console.log(info.length);
return info;
}

可以看到我们添加了一Z t F X 5 u N – (个打印语句 console.log,如果你没有 Copy 上面的代码,而是挑选手敲的话,你会发现输入 info.的时分,编辑器里面没有提示补全 length特色,因为给 info注解了 any类型,TS 编译器无法揣度此 infoH 7 t !什么类型,所以也没有对应的补全,没有补全的 TypeScript 代码是没有生命的

类型的z ~ 5 d } 9函数?

那么考虑一R ? M u 3 = , ~下,这儿该怎样做类型l U x ) u _ N注解了?相信你现已有答案了,这就是我们这一节要引出的要点:“泛型” ,我8 u m : 5 g ;将它称之为 “类型的函数”,对应 JS 的函数相同,声明一个 “类型变量”,然后在类型函数代码块里面可以运用这t 8 % x ` y h 5个 “类型变量”。

一个 JS 函数如下:

function jsFunc(varOne) {
const res = varOne + 1;
return res;
}

可以看到一个 JS 函数,有一个 va& : c d JrOne参数,这个参数变– ! U +量可以在函数体中运用。接下来我们来看一下为什么我把泛型称之为 “类型的函数”,批改我们 src/G 2 d 9 Rindex.ts里面的内容:

function getTutureTutorialsInfo<T>(info: T): T {
console.log(info.length);
returb / . R { 4 / :n info;a 9 Y
}

可以看到我们给 getTutureTutorialsInfo后边加上 <T>这个类似我们上面那个 JS 函数的 (varOne),然后我们在原 JS 函数参数和回来值中运用了这个 “类型变量” T(info: T): T,这样我们就处理了上面的两个难题:

  • 我们定义了 T 这样一个 “类型变量”,并用它来注解我们的 JS 函数参数 in+ + V @ Afo和其回4 0 L F C 1来值,T 已然是一个 “类型变量”,那么接收此 “类型变量” 的 “类型的函数” – 泛型,在之后被调用的时分,我们可以依据需求指定传入的类型,比如 stringnumber等,这就保[ 9 b证了调用函数的用户来抉择 inf# 8 6 f l ( 1 * Wo的类型 T,这样参数的类型就确认了。
  • 参数和回来值类型都运W ? B l G Z Q用了 T 来做类型标明,所以参数值和回来值类型一同。

但是稍等,上面的代码在编辑器中报错了:

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

因为我们将这个函数泛型化了,明晰了泛型变量 T 是一个明晰类型,所以我们之前的 i& u * V p 2 x # snfo.length会报错,当然这儿有同学会问了,我要是这儿} _ M ` 9 T 在之后泛型 (类型的函数)调用的时分传入的是 string类型,那不是就有 length特色了嘛,很怅惘,因为 T 还可能是_ [ % t D number类型,而 number类型的变量没有 length特色,所以 TS 编译器报错了。

为了处理上面的问题,我们可以更近一步,对函数做出批改如下:

function getTutureTutorialsInf? @ 3 K Vo<T>(info: T[]): T[] {
console.log(info.length);
retI [ j [ 8 ! c Murn info;
}

这样就好啦,不只类型确认了,并且参数和回来值类型也一同,并且我们还能清 p y M j _ j H晰的运用 info.length了,因为 TS^ ~ 4 . ! E k ~ 编译器F } k i知道 info是一个数组,这个时分你在L J # [ 1 I VSCode 编辑器里面输入 info.,应该会提示你如下效果:

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

有了代码补全的 TS 充满了生机!

通过上面的比如,我们发现,其实泛型,就像是一个关于 “类型的函数” 相同,给定输入的类型变量,然后可以运用输入变量通过组合比如 T[]、进行联合O # x x R o ^类型或交叉类型操作,来作为注解类型运用。

类型函数的运用

上面我们定义了榜首个泛型 – “{ ] *类型的函数”,接下来我们来检验运用我们的泛型,在 src/index.ts中对代码做出对应的批改如下:

function getTutureTM ~ Qutori4 o I  d ] # falsIn! ! C q S e ? b Zfo<T>(info: T[]): T[] {
console.log(j Q s H info.length);
r, p ! Seturn info;
}
getTutureTutorialsInf; * A $ a 2o<string>(['hello tuture', 'hM x 5 x [ & [ vello world'])

可以看到对应 <T>定义了泛型中的类型变量,我们在调用泛型时,也对应写了 &C ! + V a E g o `lt;string>,这样 T就在 getTutureTutorialsInfo函数中就会以 string的类型被运用,参数 inz 5 + ( . & v nfo和回来值也就对应了 string[],你的 VSCoX % Xde 编辑器里面调用的效果应该如下图,将鼠标移动到 getTutureTutoria9 j H | e b v P (lsInfo上,会直接显现 getTutureTutorialsInfo函数的类型定义,可以) ! * ( W s看到现已成功将 T换成了 string

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

并且我们还了解到,泛型的运用和q [ e I i t JS 函数的调用一脉相承,更加坚定了我们 泛型 就是 “类型的函数” 的说法和认知。

注意:

  • 上面R p ?的泛型中运用的 T 变量,其实只是一个 TypeScript 界比较习惯性的用法,常用的还有 U 等,当然你也可以写成 YourT ,这儿不捆绑。
  • 上面的泛型调用时,T 接受的类型可以是任意类型,比如目标、函数等类型,不只仅限于 stringnumb5 u 8 T Y E jerX _ ) Z V

泛型,再回忆

我们在上面用了许多的翰墨来企图将泛型和 “类型的函数” 划上等号,目的是为了让你了解泛型它工作的一个本来Q e N * %相貌。了解了泛型本来相貌之后,相信比如运用泛型可以使得 TS 代码组件化,复用代码,你也能了然如胸了。

泛型是在调e 6 f `用时再捆绑类型

我们在定义泛型的时分,是一系列类型变量,如 TU等,这些变量实i g ? m + 7 . c b践的类型我们在定义的时分是不知道的,只要在进行泛型调用的时分,由用户给定实践的类型,所以这儿有) b #一种推迟声明类型的效果。

泛型是否也有多个类型变量?

那么,已然* W { S泛型可以看做是 “类型的函数”,那么函数能接收多个参数的话,我们的泛型也能接收多个类型变量,比如:

function getTutureTutorialsInfo<T, U>(info: T[],m k ! Y profile: U): T[] {
console.log(infh W e u % K 2o.length);
conO e X 4 R d  V Qsoy ) [le.log(profile)% p k;
return info;
}
getTutureTutorialsInfo<string, object>(['hello tuture'], { username: 'tuture'})

可以看到,我们批改了 getTuturI U ?eTutorialsInfo函数的泛型定义,添加了一个新的类型变量 U,并用 U来注解了函数的第二个参数 profile的类型。

同样9 u v J,在调用 getTutureTutorialsInfo函数的时分,我们也需求传入两个类型变量,这儿我们的 profile被认为是一个 object类型。

匿名函数泛型?

在之前的内容中,我们通过命名函数来说明晰泛型,那么匿名函数怎样运用泛型了?其实和命名函数类似,只不过匿名函数是如下方法:

const getTutureT] J P # lutorialsInfo: <T>(info: T[]) => Tv % e[] = (info) => {
console.log(info.length);
return infoV j U E k - z & 8;
}
// 或许
const ge^ } O n ] 1 tTutureTutorialsInfo: &l} K - e  Tt;T>(info: T[]) => T[] = function (info) {
console.lo? p z E : 1 ) *g(info.length);
return info;
}

我们直接给匿名函数被赋值的变量进行匿名函数的注解,并加上泛型,你应该回想Q ( Z 4 F j q起之前给一个变量注解函数类型时的姿势:

(args1: type1, args2: type2, ..., args3: type3) => returnType

而匿名函数泛8 | X 4 G B型只不过在之前加上了 &ltt / C J _ & = I;T>类型变量,然后可以用于注解参数和回来值。

泛型默许类型参数?

已然我们宣称泛型是关于 “类型的函数”,为了更加深入的论证我们这个观念,我们再进一步& d ;

我们都知道函数存在默许参数一说,那么作为 “类型的函数” – 泛型,是否也有默许类型参数这一说了?不好意思,还真的有!我们来看个比如:

functS ; w } d / Y 3ion getTutureTutorialsInfo<T, U = number>(info: T[], profile: U): T[] {
console.log(info.length);
console.log(profi- + : - 2 ile);
return info;
}
getTutureTutorialsInfo&d x Y y b Z U Zlt;string, string>(['hello world'], 'hello tuture')

~ v C w 5 N 0 9够看到我们给类型变量 U一个默许的类型参数 number(还记得 ES6 里/ 2 R * / – Q _边有默许值的参数有必要靠后放置嘛?)

之后我们T k C , [在进行泛型调用的时分,却给 U传了 string类型,把这段代码放到 src/index.ts里面,应该不会报错,并且编辑器里面有杰出的提示:

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

泛型,继续前进

接下来我们继续深化泛型,回答之前文章里的一些疑问,比如:

  • 泛型数组
  • 类泛型

一同我们还会了解一些新的概念,比如:

  • 接口7 k m v / 2 0 = Q泛型
  • 类型别号泛型
  • 泛型捆绑

处理遗留的问题

泛型数组

这个我们现已在上面的比如顶用到了,泛型实践上定义了一系列类型变量,然后我们可以对这些类型变量做任意的组合以习惯各N E M d种不同的类型注解需求,其间一个组合比便当是泛型数组? j p m – 某个类型变量的数组形态,也就是我们上面提到的 info: T[],其间 T[]就是泛型数组。

当然泛型数组的表达方法还有另外+ H } P z d =一种:

Array<T>

即以泛型调用的方法回来一个关于泛型变量T的数组类H i V # v . ;型。所以我们的 getTutureTutorialsInfo函数可以写成如下姿势:

function getTutureTutorialsInfo<T>(info: Array<T>): Array<T> {
console.lQ } z * R Q 5 /og(info.lH } b N u W /  mength)a  S v;
return ins T r ( C 0 W G Rfo;
}
getTutureTutorialsIC k y ( !nfo<string>(['helle d B Co tuture', 'hello woH x H G { Vrld'])

类泛型

类泛型的方法和函数泛型类似,我们来看一个类泛型的界P 0 w ! , } d ) k说的调用,在M – a src/index.ts里面额定添加下面的内S _ _ b ` r + z容:

// 上面是 getTutureTutorialsInfo 泛型函数的定义和调用
class Tuture<T> {
info: T[];
}
let tutorH / & p @ @ !ial = new Tuture<string>()
tutorial.info = ['hello world', 'hello tuture'];

类泛型的定义也是在类名之后添加 <T>这样的办7 @ ` 0 { h法,然后就可以在类中运用 T类型变量来注解类型。而类泛型的调用和函数泛| + H型的调用类似。

学习了类泛型,我们再来解析一下在上一篇p % p : 4 m c o文章H | h / * } e中提到的那个 TodoInput组件,类似下面这样:

class TodoInput extends Reac; | { - h { 4 K 8t.Component<TodoInputPrr , I f 3 ^ - O Iops, TodoInputState> {
// ... 组件D # ` {内容 
}

这个实践上分为两个部分,首要是 ReacR i G b o x zt.Component组件基类的类泛型调用,然后是 TodoInput集成自这个类泛型。c c u ` l !因为派生类 TodX I I ; 3 n % ~ ^oInput可以获取到父类的特色和方法,所以在 TodQ / # ` W [ BoInput中运用的 this.propsthis.state在被类型注解之后,就可以在编码时自动补全,你在写代码的时分应该可以享受到如下优点:

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

打开新篇章

了解了函数泛型、类泛型,你有可能有一点主意了关于泛型,是不是我们之前的许多说明过的内容,如类型别号、接口等。你想对了!c G M * q D }TS 会在尽可能多的当地,能用泛型就b 2 5 p b a z ^ 8用上泛型,因为泛型可以将代码组件化,, f b | u b便当复用,全部智能的编译器,能不让你多写的东西,就绝对不会让你多写,通通用泛型给整上n q P 9 ^ d

接口泛型

在了解接口泛型之前,我们先来& ) E 3 T Q看一个接口是怎样写的,在 src/index.ts里面添加如下代码:

interface Profile {
username: string;
nickName: string;
avatar: sv h n Ktring;
age: string;
}

一般我们的 Profile类似上面的内容,但是有时分有些字段会依据需求的不同而不同,比如 age这个字段,有些人S O M # J m喜爱定义成数字类型 number,有些人喜爱定义成字符串类型 string,所以这又d N | Q / Z是一个推迟赋予类型的比如,可以凭仗泛型来处理,我们批改一d * {下上面的代码:

interface Profile<T> {
username: string;
nickName: string;
avatar: string;
age: T;
}
type ProfileWithAge = Profile<striE J . Tng>

可以看到,接口泛型的声明和调用与函数、类泛型的类似,它容许Z b v E ~ [你在接口里面定义一些特色,运用类型变量来注解,在调用时指明这个特色的类型。+ n 0 B h N

类型别号泛型

因为在t 6 e d V 3 j许多场景下,类型别号和接口充任类似的角色,所以在了解完接s % ; # q口泛型之后,B A 7 [ G * V我们有必要U s ^ k y T `来了解学习一下类型别号怎样结合泛型运用,和接口类似,将上面的接口泛型 PM l X ; f B ` U rrofile用类型别号重写如下:

type Profile<T> = {
uS z )sername: string;
nickName: string;
avae z q Y x 9 ltar: string;
age: T;` Y f u B ! c
}
type ProfileWithAge = Profile&O q ]lt;string>

可以看到,底子一同!

泛型捆绑

我们来处理之前的一个遗留问题,那就是即使我运用了泛型,我仍是不知道某个被泛型的类型变量注解的变量的一个结构是怎样样的即:

function getTutureTutorialsInfo<T, U>(info: TW 0 q Q 3[], profile: U): T[] {
console.log(info.length);
console.log(profile);
return info;
}
getTutureTutorialsInfo<string, object>(['hello tutureC w  b X 2 $': @ = v], { username: 'tuture'})

上面我们用类型变量 U注解了 profile参数,但我们在 T t g 5 ^运用 profile的时分,依然不知道它是什么类型,也就是说泛型尽管处理了类型的可复用性,但是仍是不能让我们写代码时获得自动补全的才能

重申:没有补全的 TypeScript 代码是没有生命的!

那么我们怎样让在既运用泛型的一同,还能获得代码补全了?答案相信你也猜到了, 那就是我们这一节要讲的泛型捆绑。
我们批改 srN = U U xc/index.ts 里面的代码如下:

type Pro` f V E - )file<T&gtk W l D W; = {
username: string;
nickName: string;
avatar: string;
age: T;
}
function getTutureTutorialsInfo<T, U extends Profile<string>>(info: T; X M v y[], profilem % ] 2: U): T[] {
console.log(S m 8 U sinfo.length);
consoleL r -.log(profile);
return info;
}

可以看o ~ ) p (到,我们复用了之前定义的 getTutureTutorialsInfoProfile,但是在 getTutureTutorialsInfo泛型中第二个类型变量做了点改进,之前只是单纯的 U,现在是 U extendsS C d N J F Profile<string>Profile<string>标明调用类型别号泛型生成一个 agestrii F [ng的新类型别号,然后通过 U extends ...的方法,用 Profile<string>来捆绑 U的类型,也就是 U有必要至少包括 Profile&l) A F nt;string>的类型。

这个时分,我们在 VSCode 编辑器里面检验输入 profile.,应该可以神奇的发现,有了自动补全:

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

并且还能了解到 agestring特色!

再次!有了代码补全的 TS 充满了生机!

当然这儿的用于捆绑的 Profile<string>可以是一个类型别号,也可以是一个接口,也可以是一个类:

class Profile<T>  {
username: string;] d I ? n O q
nickName: string;
avatar: string;
age: T;
}
// 或许
interT Y R Eface Profile<T>  {
username: string;
nickName: string;
avatar: string;
ageN I F U M B % d [: T;
}
/; o Y 1 Z h ?/ 或许
type Profile<T> = {
username: string;
nickName: string;
avatar: string;
age: T;
}

更近一步,这儿的用于捆绑类型变量的类型可以} N O M是一些更加高级的类型如联合类型、交叉类型等:

type Profile<T> = {
usN 1 g _ X Tername: string;
nickNQ . I ]ame: string;
avatar: string;
agE r M e A ke: T;
}
type Tutud : b q Y | G _ 0re = {
github: strn O m | ,ing;
remote: string[];
}
function get0 { f H t - eTutureTutorialsInfo<T, U extends Profile<string> & Tuture>(info: T_ R Z * . . / f -[], profile: U): T[] {
console.log(info.length);
consol@ _ 9 % . Je.log% 8 d(profile);
reD - & U T xturn info;
}

可以看$ 6 j ; , 8到我们运用了 Profile<string&g$ - ) ^ 4t;TU m 2 *uture的交叉类型来捆绑 U,在我们的 VSCode 编辑器里面应该会有如下补{ ) W )全效果:

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

深化实践,注解结构函数

在了解泛型的根底知识,并且结合函数、接口、类型别号和类进行结合运用o 5 * L之后,相信你对怎样运用泛型现已有了一点经历了。

而了解了泛型,你就可以初步检验深化 TS 类型编程的世界了!接下来3 z W f @ & u U W我们初步深化一下高阶的 TS 类Z M Y A型编程知识,并检验说明一些比较边{ / J Y O F m V缘状况怎样进行类型注解。

我们需求一个 createIn6 G !stance函数,它接收一个类结构函数,然后回来此类的实例,并能在调用之后获得杰出的代码补全提示(!很重要),并且此函数还需求有足够好的通用功能处理任意结构函数(!泛型) 。我们检验在 src/index.ts 里面编写一个类以及一个创建此类实例的方法:

class Profile<T> {
username: string;
nickName: string;
avatar: stri; a l r x Ang;
age: T;
}
class TuturePro, b f U + J 8file extends Profile<stringY f A # a A . ~ p> {
github: string;
remote: string[];Z F Z K q f
}
function createInstancev r x A 2 i ](B) {
return new B();
}
const myTutureProfile = createInstance(TutureProfile);

不要问我为什么 createInstance的参数是 B,因为我们终究很 new B& 3 [ d s ` X()

当我们编写了上面这个 createInstance时,当我们检验在调用之后输入 .createInstance(TutureProfile).,发现编辑器里面没有补全提示实例化目标的相关特色如 username

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

首要我们来解析一下结构函数的姿势,因为 TS 类型是鸭子类型,是m B =依据代码的实践姿势来进行类O [ v 4 l R I型注解的。结构函数是可被实例化的函数K b x i,即可以通过 new XXX() 进行调X ! 6用来创建一个实例,所以结构函数的注解应该类似这样:

interface ConstructorFunE D 8 ^ /ctK 7 +ion<C> {
new (): C;
}

即形如 new (): C的函数方法,标明可以通过调用 new XXX()生成一个 XXX的实例。即某个类:

cl_ 5 O kass Profile<T> {
username: string;
nickName: string;
avatar:- $ v * string;
age: T;
}

我们注解其结构函数类似下面:

const profileConstructor: ConstructorFu& A  _ Nnction<Profile<strini g A H ug>> = Profile;

这儿有同学还记得嘛G e = ] G h – % 3,我们在上一篇文章中讲到一个类在声明的时分会声明两个东西:1)用于注解此类实例的类型 2)以及此类的结构函数。这个比如是用来表达C E q Z X类在声明时声明的这两样东西的最佳比如之一即:

  • ConstructorFunction接口泛X % { _ B t J 4 !型接收的 C用来注解 newa R z ()生成的实例,此为榜首:用于注解此类实例的类型。
  • 用于注解 Profile的结构函数的类型 ConstructorF9 + J + o _unction<Profile<string>>,在注( S ; c f L g a /pr/ v H % b p W n =ofileConstructor变量之后,其初始化赋值是 Profile本身,并且你可以在你的 VSCode 编辑器里面编写上面的代码,应该不会报错,这说明晰第二:声明晰此类的结构函数。

了解了结构函v p m : L p % 数怎样进行类型注解之后,我们来结束第三点要求,让这个 createInstance更具通用性,二m U a话不说Z [ =,泛型走起!终究代码如下:

class Profile<T> {
username: string;
nickName: string;
avatar: string;
age: T;
}
class TutureProfile extends Profile&l; _ at;string> {
github: string;
remote: string[];
}
interfacH L j M 9e ConstructorFunctZ X ` 3 U 7 V [ Yion<C> {
new (): C;
}
function createInstance<A extends Profile<string&gS r ` # } | A nt;>(B: ConstructorFunction<A>) {
return new B();
}
const myTutureProfile = createInstance(TutureProfile);

现在你在 VSCode 编辑j P ) Y _ E I p ?createInstance(Tutur0 | W I QeProfile)后边输入 .应该可以看到代码补全:

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型

这个) ; & A比如J I 4其实关于 extends类型捆绑那一块有点剩下,但是为了组合咱 6 # x { T D *们在这一篇里面学到的知识,所以我额定} K 3 P [ 6 4 J y把它也加上了,可以看到我们重拾了全部的代码补全,I ` H @ l . z ~ b代码补全

上面类中如P J O remote等特色会有赤色下划线是因为报了 Property 'remote' has no initializer and is not definitely assigned in the constructor.ts(25: ) 6 2 1 J C w64),字面意思就是没有初始化这些特色,这个不重要,可以通过配备移除,也可以初始化。It’s your choice!

参考资料

  • 十万字带你通晓 TypeScriptv { 3 j & e o ^:nodelover.gitbook.io/typescript/…
  • 什么是 Type predicates:fettblog.eu/typescript-…
  • 从 JavaScript 到 TypeScript – 泛型:segmentfault.com/a/119000001…
  • 深化了解 TypeScript:jkchao.github.io/M T ] . u rtypescript-…
  • TypeScript 官方 K i [文档:www.staging-typescript.org/docs/handbo…

想要学习更多精彩的实战技能教程?来图雀社区逛逛吧。

类型即正义:TypeScript 从入门到实践(四):5000字长文带你重新认识泛型