總結(jié)TypeScript在項(xiàng)目開(kāi)發(fā)中的應(yīng)用實(shí)踐體會(huì)
作者:@wangly19,本文已授權(quán)掘金開(kāi)發(fā)者社區(qū)公眾號(hào)獨(dú)家使用,包括但不限于編輯、標(biāo)注原創(chuàng)等權(quán)益。
前言
從2020年年底的時(shí)候,我開(kāi)始使用Typescript進(jìn)行項(xiàng)目的開(kāi)發(fā)。期間團(tuán)隊(duì)也開(kāi)始轉(zhuǎn)向Typescript。
在這期間,做過(guò)很多嘗試,也閱讀過(guò)一些優(yōu)質(zhì)的文章和源碼。現(xiàn)如今,大多數(shù)開(kāi)源項(xiàng)目都將Typescript做為開(kāi)發(fā)的主力軍。
在這期間,我查閱的大多數(shù)文章都是在進(jìn)行一個(gè)Typescript的基礎(chǔ)使用,開(kāi)發(fā)實(shí)踐這一塊更是少之又少,少有的一些干貨文啃起來(lái)也非常的不過(guò)癮。
相信在讀的各位收藏夾里面已經(jīng)有很多份學(xué)習(xí)Typescript的小文章都在吃灰,看了一遍但到了項(xiàng)目中依舊無(wú)從下手,該如何去進(jìn)行開(kāi)發(fā)?
獨(dú)樂(lè)樂(lè)不如眾樂(lè)樂(lè),本篇文章就從開(kāi)發(fā)的角度來(lái)聊聊,探討下Typescript在真實(shí)項(xiàng)目中開(kāi)發(fā)的實(shí)踐心得和開(kāi)發(fā)體驗(yàn)。
當(dāng)你看完文章時(shí),我建議先思考團(tuán)隊(duì)是否需要Typescript。以及Typescript是否可以解決當(dāng)前項(xiàng)目生產(chǎn)的困境。
如果對(duì)于為什么使用TypeScript產(chǎn)生疑惑,那么可以移步你為什么不使用 TypeScript?,它是一個(gè)非常棒的討論話(huà)題。
必知必會(huì)的特性
在TypeScript中,有一些好用的特性和功能對(duì)于日常開(kāi)發(fā)來(lái)說(shuō)是比較常見(jiàn)的。下面就羅列一些較為實(shí)用的知識(shí)點(diǎn)作為一個(gè)小小的備忘錄。
Readonly
有了Readonly,可以聲明更加嚴(yán)謹(jǐn)?shù)目勺x屬性,亦或者變量。
在ES6當(dāng)中,可以通過(guò)const進(jìn)行常量量聲明,切聲明后不可修改,如果進(jìn)行修改的話(huà)會(huì)直接Cannot assign to 'a' because it is a constant.進(jìn)行異常拋錯(cuò)。
雖然不能更改整個(gè)值,但是如果值是一個(gè)引用類(lèi)型的話(huà),依舊可以對(duì)其內(nèi)部的屬性進(jìn)行修改。那么從只讀的概念上來(lái)說(shuō),顯然不具備當(dāng)前的能力。
而使用Typescript當(dāng)中的readonly關(guān)鍵字對(duì)屬性或者是變量進(jìn)行聲明,那么將會(huì)在編譯時(shí)就發(fā)出告警。那么在聲明部分
image.png條件類(lèi)型(Conditional Type)
如果你不知道條件類(lèi)型,那么來(lái)看一段@vue/reactivity中的代碼吧。
export?type?DeepReadonly<T>?=?T?extends?Builtin
????T
??:?T?extends?Map<infer?K,?infer?V>
??????ReadonlyMap<DeepReadonly<K>,?DeepReadonly<V>>
????:?T?extends?ReadonlyMap<infer?K,?infer?V>
????????ReadonlyMap<DeepReadonly<K>,?DeepReadonly<V>>
??????:?T?extends?WeakMap<infer?K,?infer?V>
??????????......
其中DeepReadonly通過(guò)extends的方式繼承父類(lèi)然后通過(guò)? :表達(dá)式來(lái)進(jìn)行一個(gè)類(lèi)型三目運(yùn)算符的操作進(jìn)行一個(gè)類(lèi)型的條件判斷。
通過(guò)一個(gè)簡(jiǎn)單的案例來(lái)進(jìn)行理解,當(dāng)泛型T為string類(lèi)型的時(shí)候,那么B為1,反之為2。可以看到同樣的一個(gè)類(lèi)型,因?yàn)閭魅氲?code style="font-size:14px;background-color:rgba(27,31,35,.05);font-family:'Operator Mono', Consolas, Monaco, Menlo, monospace;color:rgb(239,112,96);">泛型T不一樣,結(jié)果自然而然的有了出入。
image.pngnamespace
命名空間(namespace)是一個(gè)比較常見(jiàn)的東西,它常用于組織一份類(lèi)型區(qū)域防止類(lèi)型之間的重命名沖突,需要配置 declare 輸出到外部環(huán)境才能夠使用,非常便捷的在于使用declare namespace在工程項(xiàng)目中可以不需要引入任何類(lèi)型而直接可以訪(fǎng)問(wèn)。
image.pngdeclare
declare是用于聲明形式存在的。
declare var/let/const用來(lái)聲明全局的變量。declare function用來(lái)聲明全局方法(函數(shù))declare class用來(lái)聲明全局類(lèi)declare namespace用來(lái)聲明命名空間declare module用來(lái)聲明模塊...
在這里需要注意的是Global augmentations have the same behavior and limits as module augmentations.
Declaration Merging
什么意思呢?
image.png大體上翻譯成大白話(huà)就是:
declare與declare global它們功能是一樣的。在d.ts中,使用declare與declare global兩個(gè)作用是相等的。
因此,在d.ts進(jìn)行declare,它默認(rèn)是全局的,在使用declare global顯得有點(diǎn)畫(huà)蛇添足了。
那么什么時(shí)候使用declare, 又什么時(shí)候使用declare global?
在模塊文件中定義
declare,如果想要用作全局就可以使用declare global完成該需求。
那么,可以來(lái)看個(gè)??栗子,看完之后就大體上懂了,都是一些比較常見(jiàn)的實(shí)例。
- 在使用
TypeScript開(kāi)發(fā)的時(shí)候想為一些API添加一些自定義的屬性,或者進(jìn)行一些覆蓋。 - 在使用
vue的時(shí)候,通過(guò)import引入的vue組件大多會(huì)提示錯(cuò)誤。
如何解決?
可以通過(guò)對(duì)模塊的定義來(lái)進(jìn)行.vue文件模塊進(jìn)行一個(gè)declare module在內(nèi)部可以將其export為相關(guān)類(lèi)型。在這里vue2和vue3不太一樣。
declare?module?'*.vue'?{
????///
????export?...
}
模塊類(lèi)型
在漸進(jìn)式的過(guò)程中,很多代碼和包都可能沒(méi)有對(duì)應(yīng)的.d.ts。因此需要對(duì)部分文件進(jìn)行.d.ts的類(lèi)型文件編寫(xiě),那么,你真的知道ES Module 和 CommonJS Module之間的導(dǎo)入嗎?
ES Module的引入方式大家都知道,但是如何對(duì)其聲明.d.ts,就看下面這個(gè)用例。
我對(duì)config/index.js創(chuàng)建了一個(gè)index.d.ts作為其聲明文件,并且導(dǎo)出了config對(duì)象。那么,我如何將類(lèi)型提供給引入方呢?
首先,知道index.js導(dǎo)出是一個(gè)對(duì)象,那么declare const一個(gè)類(lèi)型出來(lái),然后通過(guò)export = config的形式對(duì)導(dǎo)出進(jìn)行聲明。那么在通過(guò)import { xxx } from '@/config就可以獲悉具體的類(lèi)型了。
declare?const?config:?BaseConfig?&?EnvConfig
export?=?config
如圖,baseApi擁有了HTTP | HTTPS的類(lèi)型。
image.png模板字符串類(lèi)型
模板字符串是一個(gè)非常有意思的東西,它能夠?qū)ξ谋具M(jìn)行一定程度上的約束,如上面baseApi在項(xiàng)目中被定義為了HTTP | HTTPS的類(lèi)型。約定當(dāng)前值中必須包含http://或者是https://才算校驗(yàn)成功。
//?global.d.ts
declare?type?HTTP?=?`http://${string}`
declare?type?HTTPS?=?`https://${string}`
//?@/config/index.d.ts
type?baseApi?=?HTTP?|?HTTPS
同樣的,在使用dva中,也可以利用特性對(duì)type進(jìn)行namespace和action的組合,這樣在寫(xiě)dispatch時(shí),可以有一定的提示和約束能力。
image.png想看更多實(shí)踐可以看ssh的TypeScript 4.1 新特性:字符串模板類(lèi)型,Vuex 終于有救了?這篇文章深入一下。
函數(shù)重載
函數(shù)重載是一個(gè)非常常用的特性,它大多數(shù)用于多態(tài)函數(shù)。大多數(shù)同學(xué)可能都不怎么使用。但是它能夠定義不同的參數(shù)類(lèi)型。需要有多個(gè)重載簽名和一個(gè)實(shí)現(xiàn)簽名。
重載簽名:就是對(duì)參數(shù)形式的不同書(shū)寫(xiě),可以定義多種模式。實(shí)現(xiàn)簽名:對(duì)函數(shù)內(nèi)部方法的具體實(shí)現(xiàn)。
image.pnggetter/setter
get/set存取器是在class當(dāng)中比較實(shí)用的一個(gè)功能,它保證了類(lèi)中變量的私有化。在外部時(shí)時(shí)不能直接對(duì)其更改的,如果大家了解javaBean的話(huà)理解起來(lái)并不是很困難。
在class中聲明一個(gè)帶_下標(biāo)的變量,那么就可以通過(guò)get, set對(duì)其進(jìn)行設(shè)置值。
在實(shí)例中當(dāng)我們調(diào)用.name的時(shí)候,其實(shí)本身就是調(diào)用了其get的方式,而設(shè)置值時(shí),則是調(diào)用set方法,
需要注意的是,
._name值也輸出了,但是TypeScript會(huì)進(jìn)行提示你._name是私有的不允許你訪(fǎng)問(wèn)。
image.png枚舉
對(duì)于typescript思想來(lái)說(shuō),(enum)是對(duì)代碼具有侵入式的,它的實(shí)現(xiàn)方式可以看其編譯成javascript后的代碼。
image.png枚舉(enum)的使用場(chǎng)景在于可以定義部分行為和狀態(tài)。通過(guò)一個(gè)??可以來(lái)看下:
對(duì)其某個(gè)任務(wù)的行為定義在枚舉當(dāng)中,這樣做可以進(jìn)行一些狀態(tài)復(fù)用,避免在頁(yè)面寫(xiě)太多status === 1的代碼,因?yàn)闆](méi)人知道1代表什么,有什么含義,不利于維護(hù)。
將其定義成enum的標(biāo)注用于標(biāo)識(shí)狀態(tài),如:status === Status.START。
image.png枚舉可以看一篇阿寶哥的小文章一文讓你徹底掌握 TS 枚舉
泛型
image.png泛型是TypeScript當(dāng)中必知必會(huì)的一個(gè)屬性,在很多的時(shí)候,類(lèi)型推導(dǎo)在開(kāi)始時(shí)很難進(jìn)行推倒。相比于使用 any 類(lèi)型,使用泛型來(lái)創(chuàng)建可復(fù)用的組件要更好,因?yàn)榉盒蜁?huì)保留參數(shù)類(lèi)型。
泛型很多時(shí)候作用于對(duì)一個(gè)類(lèi)型的多種形態(tài)定義,能夠非常靈活的對(duì)一個(gè)類(lèi)型進(jìn)行定義和延伸推導(dǎo)。
那么,來(lái)看幾個(gè)比較簡(jiǎn)單的實(shí)例
簡(jiǎn)單的泛型
type?Generics<T>?=?{
????name:?string
????age:?number
????sex:?T
}
interface?Generics<T>?{
????name:?string
????age:?number
????sex:?T
}
image.png簡(jiǎn)單的函數(shù)泛型
function?setSex<T>?(sex:?T)?{
}
setSex<'男'>('女')
image.png泛型類(lèi)
class?Person<T>?{
????private?sex:?T;
????constructor(readonly?type:?T)?{?
????????this.sex?=?type;?
????}
}
const?person?=?new?Person<'男'>('女')
image.png對(duì)于
泛型的實(shí)踐來(lái)說(shuō),使用是需要一定理解,復(fù)雜的泛型使用會(huì)非常的復(fù)雜。
工具類(lèi)型
TypeScript當(dāng)中也提供了一些非常好用的工具類(lèi)型,能夠配合我們更好的使用工具類(lèi)型。
Readonly & Partial
Readonly可以將類(lèi)型轉(zhuǎn)換為只讀對(duì)象,使用方式是Readonly<T>。
一個(gè)實(shí)栗立即了解:
interface?Person{
????name:?string;
}
type?Person2?=?Readonly<Person>;
const?a:?Person2?=?{
????name:?'wangly19'
}
const?b:?Person?=?{
????name:?'wangly19'
}
a.name?=?'wangly19?new'
b.name?=?'wangly19?new'
image.pngRecord
Record能夠快速創(chuàng)建對(duì)象類(lèi)型。它的使用方式是Record<K, V>,能夠快速的為object創(chuàng)建統(tǒng)一的key和value類(lèi)型。
image.pngPick & Omit
Pick:主要作用是從一組屬性中拿出某個(gè)屬性,并將其返回,那么先來(lái)看一下實(shí)例。
Pick的使用方法是Pick<P, K>,如(P)類(lèi)型中擁有name,age,desc三個(gè)屬性,那么K為 name則最終將取到只有name的屬性,其他的將會(huì)被排出。
image.pngOmit:主要作用是從一組屬性中排除某個(gè)屬性,并將排除屬性后的結(jié)果返回。
Omit的使用方法是Omit<P, K>,與Pick的結(jié)果是相反的,如果說(shuō)Pick是取出,那么Omit則是過(guò)濾的效果,具體可以看下面的案例。
image.pngExclude & Extract
Exclude:從一個(gè)聯(lián)合類(lèi)型中排除掉屬于另一個(gè)聯(lián)合類(lèi)型的子集
來(lái)看下,Exclude使用形式是Exclude<T, S>,如果T中的屬性在S不存在那么就會(huì)返回。
interface?A?{
????show:?boolean,
????hidden:?boolean,
????status:?string
}
interface?B?{
????show:?boolean,
????name:?string
}
type?outPut?=?Exclude<keyof?A,?keyof?B>
image.pngExtract:跟Exclude相反,從從一個(gè)聯(lián)合類(lèi)型中取出屬于另一個(gè)聯(lián)合類(lèi)型的子集
舉一反三,如果Exclude是取差集,那么Extract就是取交集。會(huì)返回兩個(gè)聯(lián)合類(lèi)型中相同的部分。
interface?A?{
????show:?boolean,
????hidden:?boolean,
????status:?string
}
interface?B?{
????show:?boolean,
????name:?string
}
type?outPut?=?Extract<keyof?A,?keyof?B>
image.pngPartial
Partial是一個(gè)將類(lèi)型轉(zhuǎn)為可選類(lèi)型的工具,對(duì)于不明確的類(lèi)型來(lái)說(shuō),需要將所有的屬性轉(zhuǎn)化為可選的?.形式,轉(zhuǎn)換成為可選的屬性類(lèi)型。
image.png其他
TypeScript的工具類(lèi)型有很多,不只是官方提供,在日常實(shí)踐中,也會(huì)定義非常多的工具類(lèi)型。那么在了解工具類(lèi)型的同時(shí),更多的是知曉這些工具類(lèi)型是如何來(lái)的,怎么實(shí)現(xiàn)。
相信我,當(dāng)你弄懂后,你對(duì)于使用Typescript會(huì)有一個(gè)新的認(rèn)識(shí),寫(xiě)起來(lái)會(huì)更加的得心應(yīng)手。
實(shí)踐場(chǎng)景
看完了太多的理論東西,那么來(lái)看看在日常實(shí)踐中是如何真實(shí)實(shí)踐一把呢?
Dva的實(shí)踐
如果使用過(guò)Dva開(kāi)發(fā)的朋友可能知道,dispatch的類(lèi)型提示非常的艱難,因此,在開(kāi)發(fā)的時(shí)候重新定義了Dispatch的類(lèi)型,用來(lái)做一些主動(dòng)的類(lèi)型提示。
對(duì)于Modal的類(lèi)型作為一些基本定義,然后對(duì)DvaDispatch進(jìn)行部分的注入和推導(dǎo)。
type擁有modal中namespace和effects & reducers的類(lèi)型推導(dǎo)。Promise返回值的主動(dòng)暴露。
image.png- 如何使用?
//?方案一
const?dispatch:?DvaDispatch<DeskTopModel>?=?useDispatch();
dispatch<null>({
????...
})
//?方案二
dispatch<DeskTopModel,?null>({
????...
})
那么DeskTopModel是什么呢?
沒(méi)錯(cuò),就是model的類(lèi)型聲明,在其中,對(duì)每一項(xiàng)effects和reducers都進(jìn)行詳細(xì)的定義,根據(jù)這些信息就可以推導(dǎo)出當(dāng)前type的類(lèi)型了。
export?interface?DeskTopModel?{
??namespace:?'desktop',
??state:?DeskTopModelState,
??effects:?{
????getTableSourceData:?Effect
??},
??reducers:?{
????saveTableData:?Reducer<DeskTopModelState>
??}
}
對(duì)于Dva來(lái)說(shuō),很多時(shí)候都需要在Effect后做某事,這個(gè)時(shí)候有兩個(gè)方式,一是callback,另外一個(gè)就是Promise回調(diào)。
而通過(guò)Promise方式,進(jìn)行返回值的推導(dǎo)可以讓使用dispatch擁有返回類(lèi)型的能力。
image.pngDva Dispatch
/**
?*?ActionType,?推導(dǎo)當(dāng)前effect?&?reducer
?*?@default?string
?*/
type?ActionType<M?extends?Model?|?string>?=?M?extends?Model???
`${M['namespace']}/${(keyof?M['effects']?|?keyof?M['reducers'])?&?string}`?:?
string
/**
?*?dvaDispatch新增類(lèi)型
?*?@example
?*?dispatch:?DvaDispatch<Store>
?*/
export?type?DvaDispatch<S?=?void>?=?<T?=?undefined,?R?=?undefined>(action:?{
??type:?ActionType<
????S?extends?Model?
????????S?
????????:?T?extends?Model?
????????????T?
????????????:?string>,
??payload?:?any
??loading?:?boolean,
??toast?:?Taro.showToast.Option
??[key:?string]:?any
})?=>?
T?extends?Model?
????R?extends?undefined?
??????undefined?
??????:?Promise<R>?
????????:?T?extends?undefined?
????????????undefined?
????????????:?Promise<T>
Service Response 實(shí)踐
Service Response是什么?
在于后端通信時(shí),會(huì)返回很多的數(shù)據(jù),那么在使用TypeScript的時(shí)候怎么去定義這些類(lèi)型呢?又怎么在團(tuán)隊(duì)協(xié)作中進(jìn)行合作呢?
在大部分實(shí)驗(yàn)當(dāng)中,我們是這樣做的。
創(chuàng)建API命名空間
絕大多數(shù)數(shù)據(jù),都是存放在API的命名空間當(dāng)中。它的目錄如下:
--?index.d.ts
--?api1.d.ts
--?api2.d.ts
--?api3.d.ts
...
團(tuán)隊(duì)協(xié)作當(dāng)中,index.d.ts多數(shù)為公共類(lèi)型。而其他文件中的則是模塊類(lèi)型。舉個(gè)例子,Request的返回類(lèi)型。
declare?namespace?API?{
??type?commonResult<T?=?any>?=?{
????data:?T,
????code:?string,
????showMessage:?false?|?{
??????method:?'message'?|?'notification',
??????type:?'success'?|?'error'?|?'info'?|?'warning',
??????message:?string,
??????description?:?string
????}
??}
}
而對(duì)應(yīng)請(qǐng)求方案配置則對(duì)應(yīng)相應(yīng)的api文件。
如home.ts聲明了配置轉(zhuǎn)請(qǐng)求函數(shù)的方式。
//?#home.ts
module.exports?=?{
??getVisualizationListApi:?'GET?/service-admin/v1/visualization/table/list'
??addVisualizationItemApi:?'GET?/service-admin/v1/visualization/table/add'
}
然后對(duì)應(yīng)的在types下聲明一個(gè)新的.d.ts類(lèi)型聲明文件。比如:home.ts對(duì)應(yīng)home.d.ts。
那么在多人協(xié)作下,每個(gè)人負(fù)責(zé)的模塊本身來(lái)說(shuō)都不會(huì)沖突。在項(xiàng)目迭代管理中,大多數(shù)都是一個(gè)人對(duì)應(yīng)一個(gè)小模塊的開(kāi)發(fā)節(jié)奏,彼此不會(huì)有太大的重復(fù)。
//?#home.d.ts
declare?namespace?API?{
??type?VisualizationListResponse?=?{}
??type?VisualizationActionResponse?=?{}
}
所有的
declare namespace API都會(huì)合并。在namespace之間依舊可以使用API.xx(其他模塊的type)來(lái)結(jié)合聲明類(lèi)型。
如何使用?
在進(jìn)行namespace的聲明定義后,可以在需要使用的地方,無(wú)需任何引入直接訪(fǎng)問(wèn)API,然后通過(guò)API.VisualizationListResponse就可以訪(fǎng)問(wèn)到定義的VisualizationListResponse類(lèi)型。
image.png資源
- 你為什么不使用 TypeScript?
- Declaration Merging
- TypeScript 4.1 新特性:字符串模板類(lèi)型,Vuex 終于有救了?
- 一文讓你徹底掌握 TS 枚舉
- TypeScript 高級(jí)用法
- 一文讀懂 TypeScript 泛型及應(yīng)用( 7.8K字)
- 在線(xiàn)Typescript,Playground
- utility-types
如何深入學(xué)習(xí)TypeScript?
當(dāng)了解TypeScript后,想學(xué)習(xí)進(jìn)階的使用方式,可以看看一些類(lèi)型庫(kù)的源碼,這些源碼內(nèi)很多TypeScript的操作都能夠在其中看到。
比較好的如:utility-types, 里面有一些實(shí)用的基本類(lèi)型,可以對(duì)源碼進(jìn)行閱讀,閱讀難度不大,多動(dòng)手實(shí)踐下就會(huì)對(duì)類(lèi)型有一個(gè)更加清晰的明確。
總結(jié)
TypeScript是一把雙刃劍,對(duì)開(kāi)發(fā)者來(lái)說(shuō)具有一定門(mén)檻,在使用不當(dāng)?shù)臅r(shí)候,其實(shí)對(duì)于項(xiàng)目來(lái)說(shuō)會(huì)變得更加的復(fù)雜,可讀性并沒(méi)有過(guò)多的提升。
根據(jù)自身團(tuán)隊(duì)的實(shí)際情況,慢慢推動(dòng)TypeScript的基建,保持當(dāng)前生態(tài)體系下的框架和庫(kù)對(duì)TypeScript的支持度良好的情況下逐步替換到TypeScript是一個(gè)不錯(cuò)的選擇。
打個(gè)比方:如果你現(xiàn)在使用的是vue2,那么不妨可以考慮下,用TypeScript寫(xiě)組件真的好嗎?
TypeScript上手需要一定的學(xué)習(xí)的學(xué)習(xí)成本和任務(wù)負(fù)擔(dān),并不是說(shuō)會(huì)javaScript就會(huì)TypeScript,其中OOP的思想來(lái)說(shuō),對(duì)團(tuán)隊(duì)成員其實(shí)是有一定的影響的。尤其是在敏捷項(xiàng)目開(kāi)發(fā)下,影響還是蠻大的。
因此,如果項(xiàng)目迭代本身高頻快,那么在估量開(kāi)發(fā)需求時(shí),質(zhì)量和效率很明顯并不能兼得之。可以慢慢的進(jìn)行推動(dòng)。
TypeScript不會(huì)防止屎山的出現(xiàn),也沒(méi)有大多數(shù)人傳言中的那么香。只是很多吹捧的人會(huì)把屎山說(shuō)香。它只是一個(gè)類(lèi)型系統(tǒng),并沒(méi)有傳的那么神乎其神,能做的只是杜絕了很多奇技淫巧,讓代碼可以在一個(gè)較為正常的環(huán)境下進(jìn)行開(kāi)發(fā)。
如何推動(dòng)?
- 進(jìn)行
TypeScript的分享,幫助團(tuán)隊(duì)成員加深對(duì)TypeScript理解。 - 使用
TypeScript進(jìn)行公共組件和方法的書(shū)寫(xiě)和切換。 - 對(duì)目前使用的框架和庫(kù)進(jìn)行
TypeScript最佳實(shí)踐。 - 對(duì)
TypeScript進(jìn)行基礎(chǔ)類(lèi)型的定義,方便團(tuán)隊(duì)成員使用。 - 關(guān)注
TypeScript新動(dòng)向,了解新特性。
