一、ts的靜態(tài)檢查
參考為什么要使用TypeScript?有哪些情景請(qǐng)簡(jiǎn)單介紹一下,或者來(lái)個(gè)例子?
TS對(duì)JS的改進(jìn)主要是靜態(tài)類型檢查,靜態(tài)類型檢查有何意義?標(biāo)準(zhǔn)答案是“靜態(tài)類型更有利于構(gòu)建大型應(yīng)用”。為什么靜態(tài)類型有利于構(gòu)建大型應(yīng)用?我總結(jié),利在兩點(diǎn)。
1.靜態(tài)類型檢查可以做到early fail
即你編寫(xiě)的代碼即使沒(méi)有被執(zhí)行到,一旦你編寫(xiě)代碼時(shí)發(fā)生類型不匹配,語(yǔ)言在編譯階段(解釋執(zhí)行也一樣,可以在運(yùn)行前)即可發(fā)現(xiàn)。針對(duì)大型應(yīng)用,測(cè)試調(diào)試分支覆蓋困難,很多代碼并不一定能夠在所有條件下執(zhí)行到。而假如你的代碼簡(jiǎn)單到任何改動(dòng)都可以從UI體現(xiàn)出來(lái),這確實(shí)跟大型應(yīng)用搭不上關(guān)系,那么靜態(tài)類型檢查確實(shí)沒(méi)什么作用。
舉例參考隨著 JavaScript 越來(lái)越完善,還有必要學(xué)習(xí) TypeScript 嗎? - 動(dòng)感小菜刀的回答 - 知乎
console.log('index', index); // output is 1
for (var i = index; i <= 2; i++) {
console.log("i", i);
}
/* two lines printed from the inside console.log, result:
* i 1
* i 2
*/
for (var i = index + 1; i <= 2; i++) {
console.log("i", i);
}
/* nothing printed from the inside console.log */
第二個(gè)for loop結(jié)果顯然不正確,因?yàn)閼?yīng)該loop一次打印出一行 "i 2"。但事實(shí)上運(yùn)行結(jié)果卻什么都沒(méi)打印出來(lái)。原因是變量index是string “1”,而不是number 1。 "1"+1 = "11","11" < 2 是false我覺(jué)得這是個(gè)很好且簡(jiǎn)短的例子體現(xiàn)出Type Check的重要性。如果用typescript的話transpile過(guò)程中會(huì)報(bào)錯(cuò),text editor也會(huì)高亮錯(cuò)誤,因?yàn)椤皊tring + number”相加是不合理的
2.類型就是最好的注釋
靜態(tài)類型對(duì)閱讀代碼是友好的,比如我們舉個(gè)例子 jQuery API Documentation 這是大家都非常喜歡用的jQuery.ajax,在這份文檔中,詳盡地解釋了類型為object的唯一一個(gè)參數(shù)settings,它是如此之復(fù)雜,如果沒(méi)有文檔,我們只看這個(gè)函數(shù)聲明的話,根本不可能有人把這個(gè)用法猜對(duì)。
針對(duì)大型應(yīng)用,方法眾多,調(diào)用關(guān)系復(fù)雜,不可能每個(gè)函數(shù)都有人編寫(xiě)細(xì)致的文檔,所以靜態(tài)類型就是非常重要的提示和約束。而假如你的代碼像jQuery這樣所有函數(shù)基本全是API,根本沒(méi)什么內(nèi)部函數(shù),而且邏輯關(guān)系看起來(lái)顯而易見(jiàn),這確實(shí)跟大型應(yīng)用搭不上關(guān)系,那么靜態(tài)類型對(duì)閱讀代碼確實(shí)也沒(méi)什么幫助。
總的來(lái)說(shuō),現(xiàn)代編程語(yǔ)言設(shè)計(jì),很多特性已經(jīng)有非常成熟的理論支持了,如果我們重視計(jì)算機(jī)基礎(chǔ),那么一些語(yǔ)言的適用場(chǎng)景就像是拼積木,可以用幾句話概括。像是TS對(duì)JS這樣,只是單一特性變化。

二、rename
參考有了Babel的話還在使用TypeScript的優(yōu)勢(shì)在哪?
隨便說(shuō)一點(diǎn)公司現(xiàn)在的前端項(xiàng)目大概有十幾萬(wàn)行代碼,各種從后端拿到的數(shù)據(jù)類型有上百種以前后端接口一改,要改字段,瞬間懵逼。全局搜索,一個(gè)個(gè)改,各種牽扯到的東西改下來(lái)再測(cè)試一頓估計(jì)小半天沒(méi)了。用了 TypeScript 之后,把數(shù)據(jù)對(duì)應(yīng)的 interface 改掉,然后重新編譯一次,把編譯失敗的地方全部改掉就好了。而且在優(yōu)秀的 TypeScript 架構(gòu)中,業(yè)務(wù)開(kāi)發(fā)基本不需要寫(xiě)類型,所有外部輸入的類型都可以自動(dòng)拿到,只需要把一些 local variable 和 output 的類型定義一下就好了,基本跟手寫(xiě) ES 6 沒(méi)有區(qū)別。寫(xiě)代碼的過(guò)程中各種錯(cuò)誤在越早期修改的成本就越低。試想沒(méi)有靜態(tài)檢查跑一遍代碼進(jìn)某個(gè)奇怪的 case 才能復(fù)現(xiàn)的錯(cuò)誤在寫(xiě)代碼時(shí)期就直接給你的個(gè)錯(cuò)誤提示,將是多么省時(shí)省力省錢(qián)。
對(duì)一個(gè)傳統(tǒng)的js程序,你執(zhí)行一次最簡(jiǎn)單的rename重構(gòu)試試?除非是局部變量,否則我是沒(méi)那個(gè)膽量。我以前這么改的時(shí)候它連node_modules中的東西都給我改了,后來(lái)再也不敢做超出局部變量范圍的重構(gòu)了。而ts用了一年多了,java中能做的大部分重構(gòu)我都做過(guò),一次都還沒(méi)有給我惹禍。當(dāng)然,如果你都沒(méi)做過(guò)重構(gòu),任何程序都是一次寫(xiě)出來(lái)就完美,我覺(jué)得你肯定是Jeff Dean,來(lái)知乎未免太屈才了
三、【校招面試】關(guān)于Typescript和ES6的對(duì)比?
具體情況:回憶起當(dāng)時(shí)的面試場(chǎng)景,確實(shí)很大一部分時(shí)間是關(guān)于這個(gè)問(wèn)題的(簡(jiǎn)歷里寫(xiě)了自己技術(shù)棧主要用TS寫(xiě)react)我答了很多,包括從 靜態(tài)類型可以幫助我們?cè)谶\(yùn)行前發(fā)現(xiàn)一部分潛在的bug,幫助IDE做代碼提示,更容易讓學(xué)JAVA、C++的開(kāi)發(fā)者開(kāi)發(fā)前端。但面試官始終覺(jué)得我沒(méi)有說(shuō)出他想聽(tīng)到的答案,整個(gè)交流的過(guò)程中,面試官說(shuō)了許多讓我很不認(rèn)同的點(diǎn),包括他覺(jué)得代碼檢查可以有ESlint,智能提示是IDE是事和typescript本身無(wú)關(guān),然后他說(shuō)TS這種強(qiáng)類型的類型限制對(duì)項(xiàng)目開(kāi)發(fā)確實(shí)有好處,但這都不是他想聽(tīng)到的點(diǎn)。說(shuō)實(shí)話,我當(dāng)時(shí)有些無(wú)奈的,我覺(jué)得ESlint這種只是代碼風(fēng)格和低級(jí)的錯(cuò)誤檢查,給動(dòng)態(tài)語(yǔ)言的代碼提示是一件很困難的事情,所以需要靜態(tài)類型的幫助,我還和他指出了TS嚴(yán)格來(lái)說(shuō)是靜態(tài)類型并不是強(qiáng)類型。
第二通電話里,我和他又聊了這個(gè)事情,他覺(jué)得TS最大的價(jià)值是引入了 接口、類、繼承的編程思想,這就是他想聽(tīng)到的答案,我特別迷惑,我問(wèn)了他 ES6的語(yǔ)法里也有類和繼承,這應(yīng)該不是TS的特性吧,他的回答是 ES6的類和TS的類的實(shí)現(xiàn)完全不一樣,然后讓我回去好好看看。他覺(jué)得我太自信了,很多東西還不是很懂。說(shuō)真的,我特別無(wú)奈,TS是要通過(guò)tsc轉(zhuǎn)換的,可以直接轉(zhuǎn)到ES5或是ES6,這本身只是語(yǔ)法糖和類的思想的問(wèn)題,為什么會(huì)有完全不一樣這種說(shuō)法呢。而且,我電話里還講了本質(zhì)上我覺(jué)得TS和ES6并不是兩個(gè)完全對(duì)立的東西,ES指的應(yīng)該是關(guān)于語(yǔ)言的語(yǔ)法規(guī)范,TS完全就兼容ES各個(gè)版本的語(yǔ)法,所以才說(shuō)TS是JS的超集。而面試官始終堅(jiān)持TS和ES6是兩種完全不一樣的開(kāi)發(fā)模式,他說(shuō)他本身更多的是用ES6去開(kāi)發(fā),不怎么寫(xiě)TS,他覺(jué)得我太自信了,很多東西還要去學(xué)習(xí)。我真的特別無(wú)奈,我想知道對(duì)于這個(gè)問(wèn)題到底應(yīng)該怎么樣回答。
不考慮「面試」這個(gè)問(wèn)題本身的話,題主的理解確實(shí)是基本正確的,并且「面試官」的理解問(wèn)題很多(假設(shè)題主的引述真實(shí)的話)。
1.包括他覺(jué)得代碼檢查可以有ESlintESlint
檢查的是代碼規(guī)范,而非程序邏輯本身(假設(shè)語(yǔ)法正確的情況下)。
2.智能提示是IDE是事和typescript本身無(wú)關(guān)
很不巧,TypeScript 自帶了完備的 Language Service(Microsoft/TypeScript),功能包括但不僅限于什么時(shí)候報(bào)什么錯(cuò),輸了什么之后給什么提示,選擇了某個(gè)提示之后自動(dòng)補(bǔ)全什么內(nèi)容。IDE 除了簡(jiǎn)單整合一下 UI 之外真的沒(méi)什么事。相反,IDE 如果自己做效果還可能更渣。
3.然后他說(shuō)TS這種強(qiáng)類型的類型限制對(duì)項(xiàng)目開(kāi)發(fā)確實(shí)有好處
TypeScript 不是「強(qiáng)類型」,是「靜態(tài)類型檢查」的「弱類型」。
4.他覺(jué)得TS最大的價(jià)值是引入了 接口、類、繼承的編程思想
「類」和「繼承」都不是 TypeScript 引入的,直接作為 JavaScript 的超集包含進(jìn)來(lái)了而已。TypeScript 的 interface 是 Structural 的(其實(shí)連 class 也是 Structural 的),只要恰好長(zhǎng)一樣就是相同類型。interface 的引入就只是方便 Duck Typing 下的檢查而已,和 Java、C# 那樣的 Nominal 的 interface 差別明顯。另外,除了 Duck Typing 外,JavaScript 并不是沒(méi)有「面向接口」編程的實(shí)踐,實(shí)際上 ES2015 中 Symbol 的一個(gè)重要作用就是承擔(dān) interface 的職責(zé),比如 Symbol.iterator,帶有這個(gè)屬性就等價(jià)于「面向接口」編程中的實(shí)現(xiàn)了 Iterable 這個(gè) interface,從而能夠被 for-of 使用?,F(xiàn)在的 Proposal 里面還有 Symbol.asyncIterator 和 Symbol.observable,當(dāng)然很顯然的就是定義了 AsyncIterable 和 Observable 這兩個(gè) interface 出來(lái),只是沒(méi)有單獨(dú)設(shè)置 interface 這個(gè)關(guān)鍵詞而已。
事實(shí)上 interface 和 implements 也已經(jīng)是 JavaScript 中的 Future Reserved Words,未來(lái)很有可能會(huì)引入到規(guī)范中,目前在 ES Discuss 中對(duì)于引入 interface 也有廣泛討論,但是阻力也不小。
5.他的回答是 ES6的類和TS的類的實(shí)現(xiàn)完全不一樣,然后讓我回去好好看看讓他回去自己好好看看
TypeScript 的 Design Goals(Microsoft/TypeScript),然后看實(shí)現(xiàn)的時(shí)候記得把 target 設(shè)成 esnext,能剩下的東西都不是 TypeScript 的東西。
6.而面試官始終堅(jiān)持TS和ES6是兩種完全不一樣的開(kāi)發(fā)模式
ES6 并非只有一種開(kāi)發(fā)模式,同樣的 TypeScript 也并非只有一種開(kāi)發(fā)模式。最大的可能是本身對(duì) JavaScript 不了解所以誤認(rèn)為 TypeScript 里文檔里給的概念就是 TypeScript 的概念。TypeScript 只是為 JavaScript 中本身就存在的使用方式提供了對(duì)應(yīng)的類型標(biāo)注,所有在 TypeScript 中能夠使用的開(kāi)發(fā)模式,在 JavaScript 中一定是本身就存在的。要認(rèn)為 TypeScript 能產(chǎn)生和 JavaScript 完全不一樣的開(kāi)發(fā)模式,那只能認(rèn)為是完全不了解 JavaScript。
四、TS遇到的問(wèn)題
參考你為什么不使用 TypeScript?
注意TS沒(méi)坑,框架沒(méi)坑,TS+框架會(huì)有很多坑你必須學(xué)習(xí)框架+全家桶的各種暴露出來(lái)的d.ts(還好用的是React,生態(tài)良好,生態(tài)差的在下一條出現(xiàn)),重新配置webpack,并解決各種沖突,比如在webpack中設(shè)置了src為@方便我們引入文件,然而Typescript不識(shí)別會(huì)報(bào)錯(cuò),你需要解決這種沖突,此類沖突很多,還有如果TS徹底替代babel會(huì)出問(wèn)題,比如不能享受babel-plugin-import這種按需引入的Babel插件了,最好的方法是TS+Babel,這又是額外的復(fù)雜度,再比如prettier美化插件,雖然支持了TS但是會(huì)出現(xiàn)各種各樣的花式美化,還有各種各樣的坑爹的地方就不提了,需要有一段很惡心的過(guò)程去一點(diǎn)點(diǎn)踩坑,我對(duì)那段的記憶反正是痛苦的,但是醉心于TS第一條提到的優(yōu)點(diǎn),還是把這shi吃了。。。而且還時(shí)不時(shí)踩到shi。。對(duì)應(yīng)題主的問(wèn)題,我當(dāng)初有放棄的想法,就是因?yàn)門(mén)S與框架的結(jié)合方面會(huì)出現(xiàn)大量細(xì)枝末節(jié)的問(wèn)題,這是阻止人們使用的一大障礙
TS到底適合什么場(chǎng)景我個(gè)人認(rèn)為1. 中大型項(xiàng)目,需要長(zhǎng)期維護(hù)的項(xiàng)目,底層庫(kù)or框架2.上一條的前提是,項(xiàng)目主要依賴的類庫(kù)對(duì)TS支持良好,最好有業(yè)界的先行案例所以用一些冷門(mén)框架或者小項(xiàng)目的時(shí)候我是不用TS的,TS沒(méi)問(wèn)題,框架也沒(méi)問(wèn)題,框架+TS會(huì)有很多問(wèn)題。