您好,登錄后才能下訂單哦!
今天小編給大家分享一下TypeScript應(yīng)該盡量避免的語(yǔ)法有哪些的相關(guān)知識(shí)點(diǎn),內(nèi)容詳細(xì),邏輯清晰,相信大部分人都還太了解這方面的知識(shí),所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來(lái)了解一下吧。
枚舉提供了一組常量。在下面的例子里,HttpMethod.Get 是字符串 ‘Get’ 的名字。HttpMethod 類型和一個(gè)聯(lián)合類型是一樣的,如 'GET' | 'POST'。
enum HttpMethod { Get = 'GET', Post = 'POST', } const method: HttpMethod = HttpMethod.Post; method; // Evaluates to 'POST'
下面是支持使用枚舉的原因:
假設(shè),我們最終要替換 ‘POST’ 為 ‘post’。我們只要替換枚舉的值就能達(dá)成這一目的。我們其他的代碼因?yàn)橐玫氖?HttpMethod.Post ,所以完全不用改。
現(xiàn)在假設(shè),如果我們用聯(lián)合類型來(lái)實(shí)現(xiàn)這個(gè)場(chǎng)景。我們定義了聯(lián)合類型 'GET' | 'POST',然后我們決定把它們改為小寫的 'get' | 'post'。現(xiàn)在如果使用 'GET' 或者 'POST' 作為 HttpMethod 的代碼就會(huì)報(bào)類型錯(cuò)誤。我們需要把所有的代碼手動(dòng)改一遍。從這個(gè)例子來(lái)說(shuō),使用枚舉能簡(jiǎn)單一些。
這個(gè)支持使用枚舉的例子可能不是那么有說(shuō)服力。當(dāng)我們?cè)黾恿艘粋€(gè)枚舉和聯(lián)合類型的時(shí)候,實(shí)際上在創(chuàng)建以后是很少更改的。使用聯(lián)合類型,確實(shí)會(huì)帶來(lái)更多的更改成本,但是其實(shí)不是一個(gè)問(wèn)題,因?yàn)閷?shí)際上是很少更改的。即便要更改,因?yàn)橛蓄愋湾e(cuò)誤,我們并不害怕少改了。
使用枚舉的壞處是:
我們需要適應(yīng) TypeScript 的語(yǔ)法。TypeScript 應(yīng)該是 JavaScript,但是增加了靜態(tài)類型。如果我們?nèi)サ?TypeScript 的類型,我們就應(yīng)該得到一份完整有效的 JavaScript 的代碼。(譯者注:這個(gè)原因是整篇文章的核心,核心好處之一就是,你可以通過(guò) esbuild 而不是 tsc 完成你的 ts 代碼到 js 代碼的轉(zhuǎn)換,這個(gè)速度差距可能是 10-1000倍。。并且不引入 tsc,代表著少了一個(gè)可能出問(wèn)題的地方。)在 TypeScript 的官方文檔中,之前描述 TypeScript 的文檔是 “類型級(jí)別的擴(kuò)展”:即 TypeScript 是 JavaScript 類型級(jí)別的擴(kuò)展,所有 TypeScript 的特性不改變運(yùn)行時(shí)的行為。
下面是一個(gè)類型級(jí)別擴(kuò)展的例子, TypeScript 的例子:
function add(x: number, y: number): number { return x + y; } add(1, 2); // Evaluates to 3
TypeScript 的編譯器檢查了代碼的類型。然后生成了 JavaScript 的代碼。很幸運(yùn),這個(gè)過(guò)程很簡(jiǎn)單:編譯器只要把類型標(biāo)注去掉就好了。在這個(gè)例子里,只要把 :number 去掉,下面就是完美的 JavaScript 代碼:
function add(x, y) { return x + y; } add(1, 2); // Evaluates to 3
絕大部分 TypeScript 的特性都有這個(gè)特性,遵循了類型級(jí)別擴(kuò)展的法則。要得到 JavaScript 代碼,只需要去掉類型標(biāo)準(zhǔn)即可以。
然而,枚舉打破了這個(gè)法則。HttpMethod 和 HttpMethod.Post是一部分的類型。他們應(yīng)該被去除。然而,如果編譯器去除這些代碼,就會(huì)有問(wèn)題,因?yàn)槲覀儗?shí)際上在把 HttpMethod.Post 當(dāng)成值類型在使用。如果編譯器簡(jiǎn)單刪除這些代碼,這些代碼就不能跑了。
/* This is compiled JavaScript code referencing a TypeScript enum. But if the * TypeScript compiler simply removes the enum, then there's nothing to * reference! * * This code fails at runtime: * Uncaught ReferenceError: HttpMethod is not defined */ const method = HttpMethod.Post;
TypeScript 的解決方案,就是打破自己的規(guī)則。當(dāng)編譯一個(gè)枚舉的時(shí)候,編譯器會(huì)自己生成一些 JavaScript 代碼。其實(shí)很少 TypeScript 特性會(huì)這樣做,這個(gè)其實(shí)讓 TypeScript 的編譯模型變得復(fù)雜了。因?yàn)檫@些原因,我們建議避免使用枚舉,而用聯(lián)合類型來(lái)取代它。
為什么類型級(jí)別擴(kuò)展這個(gè)規(guī)則這么重要呢?
讓我們來(lái)看,這個(gè)法則在和 JavaScript 和 TypeScript 的工具鏈生態(tài)互動(dòng)時(shí),會(huì)發(fā)生什么。TypeScript 的項(xiàng)目都是從 JavaScript 項(xiàng)目繼承而來(lái)的,所以使用打包工具和編譯工具,例如 webpack 和 babel 是很正常的。這些工具都是為了 JavaScript 設(shè)計(jì)的,即便在今天,依然是關(guān)注在 JavaScript 上。每一個(gè)工具都有自己的生態(tài)。這里有無(wú)數(shù)的 Babel 和 Webpack 自有的生態(tài)的插件。
有可能讓所有 Babel 和 Webpack 以及他們的生態(tài)插件支持 TypeScript 么?對(duì)于大部分 TypeScript 語(yǔ)言來(lái)說(shuō),實(shí)際上類型擴(kuò)展規(guī)則讓這些內(nèi)容支持 TypeScript 很簡(jiǎn)單。工具只要去掉類型標(biāo)準(zhǔn),然后對(duì)其余的 JavaScript 做剩下的工具就好了。
當(dāng)對(duì)于像枚舉這樣的特性(包括名字空間 namespaces),這個(gè)事兒要復(fù)雜一些。不能簡(jiǎn)單移除枚舉。工具需要把 enum HttpMethod { ... } 轉(zhuǎn)譯 成合適的 JavaScript 代碼,因?yàn)?JavaScript 并沒(méi)有 enum 關(guān)鍵字。
這會(huì)帶來(lái)一些實(shí)際的工作量,來(lái)處理 TypeScript 自己打破自己的類型擴(kuò)展法則的問(wèn)題。像 Babel、webpack 以及他們的生態(tài)插件,都是先對(duì) JavaScript 作為設(shè)計(jì)對(duì)象,TypeScript 一般來(lái)說(shuō)只是他們支持的一個(gè)功能。很多時(shí)候,TypeScript 的支持并不能收到像 JavaScript 一樣的支持,就會(huì)有很多 Bug。(譯者注:考慮 JavaScript 實(shí)際上讓這些工具和插件的難度小很多,考慮 TypeScript,很多問(wèn)題其實(shí)變復(fù)雜了,而且這個(gè)復(fù)雜度的提升不一定是有價(jià)值的。時(shí)至今日,依然是 JavaScript 的代碼和需求遠(yuǎn)遠(yuǎn)大于 TypeScript。即便出于降低這些工具的復(fù)雜度的目的,也不應(yīng)該為了解決 TypeScript 的問(wèn)題而引入 這些問(wèn)題。最核心的運(yùn)行時(shí),依然,以及必然是 JavaScript。)
很多工具的工作主要是在處理變量聲明和函數(shù)聲明,這些事情其實(shí)相對(duì)都是比較容易做的。但是牽扯枚舉和名字空間,就不能僅僅去掉類型標(biāo)注開(kāi)始做邏輯了。你當(dāng)然可以信賴 TypeScript 的編譯器,但是很多不常用的工具可不一定考慮這個(gè)問(wèn)題了。
當(dāng)你的編譯器、打包器、壓縮器、linter、代碼格式器(譯者注:其實(shí)代碼格式器很容易造成 bug,尤其對(duì)于 TypeScript)只要發(fā)生了一個(gè)對(duì)于上述的事兒處理有問(wèn)題,是非常難進(jìn)行 debug 的。編譯器的 Bug 是非常非常難找的(譯者注:當(dāng)出現(xiàn)一個(gè) bug,你會(huì)第幾直覺(jué)認(rèn)為是編譯器的錯(cuò)誤呢?其實(shí)不使用這些特性,你的代碼是不依賴 TypeScript 編譯器的,這一點(diǎn)至關(guān)重要。)。主要這篇文章的這些文字:經(jīng)歷了幾周以后,在我的同事的幫助下,我們對(duì)于這個(gè)bug牽扯的范疇有了更深的認(rèn)識(shí)。(注意加粗字體)(譯者注:我本來(lái)花了大約兩個(gè)月的時(shí)間去研究 TypeScript 的裝飾器以及裝飾器元數(shù)據(jù),然后計(jì)劃把他們加入到我自己的框架里。但是最后沮喪的發(fā)現(xiàn),如果我引入他們,我就沒(méi)有辦法用 esbuild 了,原因是 esbuild 不計(jì)劃支持 TypeScript 的裝飾器元數(shù)據(jù),但是支持了裝飾器,但是這個(gè)支持其實(shí)也很新,而我的整個(gè)框架其實(shí)是以 esbuild 為基石的。我很沮喪,放棄了 TypeScript 的裝飾器)(譯者注:引入 tsc 是不明智的,因?yàn)?tsc 非常非常復(fù)雜。實(shí)際上,你只用類型的話,在代碼編寫階段基本也就完成了絕大部分 tsc 的事情。在最后用 esbuild 一去類型,就可以繼續(xù)了。)
名字空間類似 module,但是一個(gè)文件里可以有多個(gè)名字空間。例如,我們?cè)谝粋€(gè)文件里引入了不同名字空間的導(dǎo)出代碼,以及它們對(duì)應(yīng)的測(cè)試。(我們不建議這樣使用名字空間,這里只是作為一個(gè)探討的例子。)
namespace Util { export function wordCount(s: string) { return s.split(/\b\w+\b/g).length - 1; } } namespace Tests { export function testWordCount() { if (Util.wordCount('hello there') !== 2) { throw new Error("Expected word count for 'hello there' to be 2"); } } } Tests.testWordCount();
名字空間在實(shí)踐上會(huì)造成一些問(wèn)題。在上面的枚舉的例子里,我們看到了 TypeScript 的類型擴(kuò)展法則。通常,TypeScript 去除類型標(biāo)注,留下的就是 JavaScript 的代碼。
名字空間自然也打破了這一設(shè)定。在 namespace Util { export function wordCount ... } 代碼里,我們不能僅僅靠去除類型標(biāo)注就獲得 JavaScript 的代碼。整個(gè)名字空間就是一個(gè) TypeScript 的類型定義!在其他的代碼里使用 Util.wordCount(...) 會(huì)發(fā)生什么呢?如果我們刪除 Util 名字空間,然后生成 JavaScript 代碼,Util 就沒(méi)有了。所以 Util.wordCount(...) 也不能工作。
就和枚舉一樣,TypeScript 也不能僅僅刪除名字空間定義,而要生成一些 JavaScript 的代碼。
對(duì)于枚舉,我們建議用聯(lián)合類型來(lái)取代。對(duì)于名字空間,我們建議就用 ESM 取代就好了。雖然創(chuàng)建很多文件很麻煩。但是兩者能達(dá)成的效果是完全一樣的。
裝飾器是一個(gè)可以修改和取代其他函數(shù)或者類的方法。這里是一個(gè)從 TypeScript 官方文檔里找到的裝飾器的例子:
// This is the decorator. @sealed class BugReport { type = "report"; title: string; constructor(t: string) { this.title = t; } }
@sealed 裝飾器暗示了C# 的 sealed 裝飾器。這個(gè)裝飾器可以防止其他類繼承這個(gè)類。我們可以實(shí)現(xiàn)一個(gè) sealed 的函數(shù),然后接受一個(gè)類,修改它,讓它不能繼承這個(gè)類。
裝飾器一開(kāi)始是首先在 TypeScript 添加的,然后 JavaScript(ECMAScript)才開(kāi)始了標(biāo)準(zhǔn)化進(jìn)程。在 2022 年1月,裝飾器依然是一個(gè) ECMAScript 提案階段 2 的提案。階段 2 代表在 “draft”(起草)階段。裝飾器提案似乎一直停滯在委員會(huì)中:實(shí)際上,這個(gè)提案是在 2019 年 2 月到達(dá)階段 2 的。
我們建議在 stage 3 前,避免使用裝飾器。stage 3 指 “candidate” 階段,或者 stage 4 “finished” 階段。
有這樣的可能,即 ECMAScript 永遠(yuǎn)不完成裝飾器提案。如果這個(gè)提案不完成,裝飾器的處境就和枚舉、名字空間一樣。使用裝飾器,就代表著,打破了 TypeScript 的類型擴(kuò)展規(guī)則,并且使用這個(gè)特性,很多打包工具,可能都是有問(wèn)題的。我們不知道多會(huì)能讓裝飾器通過(guò),但是裝飾器帶來(lái)的好處并沒(méi)有那么大,所以我們選擇等待。
一些開(kāi)源的庫(kù),例如有名的 TypeORM,非常重的使用了裝飾器。我們承認(rèn),如果遵守我們的建議,就不能使用 TypeORM。當(dāng)然使用 TypeORM 和裝飾器有時(shí)候是好的選擇,但是你應(yīng)該明白這么做帶來(lái)的問(wèn)題,你要知道,目前裝飾器的提案的標(biāo)準(zhǔn)化過(guò)程可能永遠(yuǎn)不會(huì)結(jié)束。(譯者注:如果你想享受 esbuild 帶來(lái)的好處,裝飾器用的深就可能是個(gè)問(wèn)題。當(dāng)然,如果你的業(yè)務(wù)可以自閉在一套裝飾器寫的框架里,可能也不是非常大的問(wèn)題。但是,如果 JS 的裝飾器出現(xiàn),現(xiàn)有的裝飾器框架可能就有問(wèn)題了。)
TypeScript 有兩種方式讓一個(gè)類型屬性私有。老的方法是 private 關(guān)鍵字,這個(gè)是 TypeScript 獨(dú)有的。目前還有一個(gè)新的方式:#somePrivateField,這個(gè)是 JavaScript 的方式。下面是一個(gè)例子:
class MyClass { private field1: string; #field2: string; ... }
我們建議 #somePrivateField 字段。但是這兩個(gè)方法基本是等同的。但是我們建議更多使用 JavaScript 的特性。
以上就是“TypeScript應(yīng)該盡量避免的語(yǔ)法有哪些”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會(huì)為大家更新不同的知識(shí),如果還想學(xué)習(xí)更多的知識(shí),請(qǐng)關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。