TypeScript,F(xiàn)low與WebAssembly">從JavaScript到TypeScript,F(xiàn)low與WebAssembly
1155
2025-04-03
為什么說 TypeScript 是開發(fā)大型前端項目的必備語言
前言
怎么忍心怪你犯了錯,是我給你自由過了火。 -- 張信哲《過火》
總而言之,TS 如今在前端領(lǐng)域具有不可撼動的核心地位,是非常重要的前端工程開發(fā)工具。TS 是微軟在擁抱開源項目之后為軟件行業(yè)作出的突出貢獻之一。然而,TypeScript 既不能提高 JavaScript 代碼在瀏覽器中的運行效率,又不像 React、Vue 前端框架那樣可以提高開發(fā)人員的生產(chǎn)力,更不能讓你開發(fā)的前端頁面變得好看而吸引人。那究竟是什么讓它成為如此廣受歡迎的 “真香語言” 呢,是什么讓它令前端人員愛不釋手呢?如果你帶有這樣類似的疑問,請繼續(xù)閱讀接下來的內(nèi)容,本文將詳細解釋利用 TS 開發(fā)大型前端項目的優(yōu)勢。
TS 簡介
TypeScript 誕生于微軟(Microsoft),由 C# 首席架構(gòu)師、.NET(dotnet)創(chuàng)立者 Anders Hejlsberg 設(shè)計開發(fā)。TypeScript 簡單來說就是強類型系統(tǒng)的 JavaScript。它是 JS 的超集(Superset),也就是說,TS 支持 JS 中所有語法,并且還擴展了很多面向?qū)ο缶幊蹋∣OP)的一些功能。TypeScript 首個公開版本 0.8 于 2012 年 10 月發(fā)布,并受到了 GNOME 之父 Miguel de Icaza 的稱贊,不過他指出 TypeScript 的最大缺點是它缺乏成熟的 IDE 支持,例如當時唯一能支持 TS 只有運行在 Windows 上的 Visual Studio,因此 Mac 和 Linux 使用者無法有效使用 TS 編寫項目。不過,這個缺點很快被克服了。1 年之后,很多編輯器,例如 Eclipse、Sublime、Vim 等,都開始支持 TypeScript 語法。如今,絕大多數(shù)主流編輯器都能有效支持 TypeScript。而有了 TypeScript 的加持,你在 IDE 中就可以爽快的享受 TS 特有的自動完成(Autocomplete)以及類型提示(Typing Hint),以便于編寫可靠而高質(zhì)量的代碼。正如官網(wǎng)中對 TypeScript 的一句話介紹:“Typed JavaScript at Any Scale”。
面向接口編程
TypeScript 的設(shè)計理念來自于面向接口編程(IOP),一種基于抽象類型約定(Abstract Type Constraint)的程序設(shè)計模式。要真正了解 TypeScript,首先得了解面向接口編程的概念和原理。最初引入 IOP 概念的論文《面向接口編程》發(fā)表于 2004 年,它屬于面向?qū)ο缶幊蹋∣OP)體系的一部分,是更為先進和獨立的編程思想。作為 OOP 體系的一部分,IOP 更加強調(diào)規(guī)則和約束,以及接口類型方法的約定,從而讓開發(fā)人員盡可能的關(guān)注更抽象的程序邏輯,而不是在更細節(jié)的實現(xiàn)方式上浪費時間。很多大型項目采用的都是 IOP 的編程模式。
上圖是 IOP 編程模式的示意圖,在 Java 或 C# 這樣的 OOP 語言中,隨處可見這樣的處理方式。類型(Class)的方法(Method)由接口(Interface)來約定,而類型中需要實現(xiàn)(Implement)接口中定義的抽象方法。例如 IService 接口定義了 Request(url)、Auth(usr, pwd) 方法,而這個接口不管這兩個方法如何實現(xiàn),只是將方法的名稱、參數(shù)與返回值定義下來,而具體的實現(xiàn),即就是實現(xiàn)該功能的核心代碼,將在 Service 這個類中完成。
這樣注重抽象方法約定的設(shè)計模式對于構(gòu)建大型項目來說具有非常大的優(yōu)勢。首先,它不要求編程人員在程序開發(fā)早期或設(shè)計系統(tǒng)階段編寫任何核心代碼,從而編程人員可以將主要精力放在架構(gòu)設(shè)計以及業(yè)務(wù)邏輯,避免浪費過多時間在思考具體實現(xiàn)上。其次,由于 IOP 中多了接口這一抽象層,避免將核心代碼細節(jié)暴露給開發(fā)者,不熟悉該模塊的開發(fā)者可以從文檔、名稱、參數(shù)、返回值等來快速推斷程序邏輯,這大大的降低了程序復(fù)雜性以及維護成本。最后,IOP 可以讓編程變得更靈活,它天生支持多態(tài)(Polymorphic),可以作為 OOP 中類繼承(Class Inheritance)的有效替代。可能很多初學者會抱怨說這種設(shè)計理念讓編程變得臃腫,但不得不說這種附加的約束能夠讓大型項目在面對復(fù)雜性和多變性時顯得更加穩(wěn)定和反脆弱。
讀者可能會問,這跟 TypeScript 有什么關(guān)系?其實,如果你用 TS 編寫過大型項目,應(yīng)該能夠意識到 IOP 這種理念在 TS 中發(fā)揮的作用。接下來,本文將介紹 TS 的一些核心概念,這將有助于讀者進一步理解 TS 的特性以及它為何適合大型項目。
TS 核心概念
之前也提到,TS 是 JS 的超集,也就是說 JS 的語法 TS 全部都包括,而且在此基礎(chǔ)上還添加了類型系統(tǒng)。類型的定義主要是以 “變量或?qū)傩悦Q后面加冒號跟類型來制定的,即
//?JS
const?strVal?=?'string-value';
const?objVal?=?{
key:?'string-value',
value:?1,
}
const?func?=?param?=>?param?*?2;
而 TS 中類似的寫法是這樣。
//?TS
const?strVal:?string?=?'string-value';
interface?IObject?{
key:?string;
value:?number;
}
const?objVal:?IObject?=?{
key:?'string-value',
value:?1,
};
type?Func?=?(param:?number)?=>?number;
const?func:?Func?=?(param:?number)?=>?param?*?2;
看到區(qū)別了么?首先你可能會注意到 JS 要比 TS 簡潔得多。這很自然,因為 TS 加入了類型系統(tǒng),必然會增加一些額外的約束代碼。JS 是弱類型動態(tài)語言,類型之間可以自由轉(zhuǎn)換,顯得非常靈活。然而由這種靈活和自由度產(chǎn)生的編碼快感只能持續(xù)短短一段時間,當項目不斷發(fā)展,變量類型變多,模塊間的交互關(guān)系變得復(fù)雜之后,這種自由度帶來的麻煩很可能是災(zāi)難級的。坊間傳言 “動態(tài)一時爽,重構(gòu)火葬場” 說的就是這個道理。
你可能也會注意到,對象變量 objVal 被接口 IObject 約束(它是用 interface 這個關(guān)鍵詞來定義的),其中規(guī)定了它要求的屬性以及相應(yīng)的類型,因此在對其賦值或取值時不能隨意處理。同理,函數(shù)(Function)也可以用關(guān)鍵詞 type 來定義,這里定義了函數(shù)類型 Func 的參數(shù)名、參數(shù)類型,以及返回的結(jié)果類型。
跟其他主流編程語言一樣,TypeScript 同樣有基礎(chǔ)數(shù)據(jù)類型。其中大部分跟 JavaScript 的基礎(chǔ)數(shù)據(jù)類型一致,另外還添加了一些 TS 特有的類型。
基礎(chǔ)類型列表如下:
TS 中除了基礎(chǔ)類型以外,還可以自定義項目中需要的類型。這種自定義類型在 TS 中通常用接口(Interface)或字面類型(Literal Type)來定義。
接口是 TypeScript 中非常重要的核心概念。接口的定義方法非常簡單,在 TS 代碼里用 interface 關(guān)鍵詞加接口名,后跟花括號 {...},并在花括號中定義這個接口包含的屬性以及屬性對應(yīng)的類型,可以是基礎(chǔ)類型,也可以是接口(Interface)、類(Class)、類型(Type)。請注意到,函數(shù)也是類型的一種。下面用代碼舉例說明。
interface?Client?{
host:?string;
port?:?string;
username?:?string;
password?:?string;
connect:?()?=>?void;
disconnect:?()?=>?void;
}
interface?ResponseData?{
code:?number;
error?:?string;
message:?string;
data:?any;
}
interface?Service?{
name?:?string;
client:?Client;
request:?(url:?string)?=>?ResponseData;
}
以上是接口定義代碼,下面看一下如何使用。
const?client:?Client?=?{
host:?'localhost',
connect:?()?=>?{
console.log('connecting?to?remote?server');
},
disconnect:?()?=>?{
console.log('disconnecting?from?remote?server');
},
};
const?service:?Service?=?{
name:?'Mock?Service',
client,
request:?(url:?string)?=>?{
return?{
code:?200,
message:?'success',
data:?url,
},
},
};
這是一個模擬客戶端服務(wù)定義的非常簡單的代碼。可以注意到,接口定義了后面實例(Instance)client、service 的屬性(Attributes,有些文章會稱作 “字段 Field”)和方法(Methods),該接口約束下的實例必須包含對應(yīng)要求的屬性和正確類型,否則在編譯時(Compile Time)就會報錯。接口中屬性名后面帶一個問號 ? 是代表該屬性是可選的(Optional)。因此,模塊或功能首先由接口來定義,將大框架考慮好之后再實現(xiàn)接口中的核心邏輯,這也就滿足了面向接口編程中的設(shè)計模式。
而字面類型(Literal Type)主要是簡化一些比較簡單的自定義類型。其定義方式同樣非常簡單,限于篇幅就不展開講解。感興趣的讀者可以到官網(wǎng)文檔深入了解。
了解了基礎(chǔ)類型、接口、字面類型之后,我們可以開始著手用 TS 寫一些可靠的代碼了。不過為了加深對 TS 類型系統(tǒng)的理解,我們需要了解什么情況下,接口與實例的綁定是合法的? TypeScript 中的類型合法性檢測方法是如何規(guī)定?
TypeScript 采用了所謂的 “鴨子類型”(Duck Typing)策略。所謂鴨子類型,就是當兩個類型具有相同的屬性以及方法時,它們就可以看作是同一類型。例如,狗既能吃飯又能拉屎,還有兩個眼睛一張嘴;人同樣也是既能吃飯又能拉屎,還有兩個眼睛一張嘴。單從這些屬性來看,狗跟人是同一類型。但很顯然這個結(jié)論是荒唐的。人類可以說話,狗卻不能;狗可以搖尾巴,而人卻不能。但如果一個外星人到地球來訪問,TA 很可能會將人和狗分為一類,因為它們既能吃飯又能拉屎,還有兩個眼睛一張嘴(誤)。不過外星人通過選擇性的觀測兩種生物,可以快速的將它們進行歸類。
其實,鴨子類型在類型約束的同時,又帶有一些靈活性,代碼顯得靈活而精簡,不會因為嚴格的約束條件而讓代碼變得臃腫。TypeScript 用這種方式來校驗類型合法性,可以提升編寫 TS 的體驗感,繞開了傳統(tǒng) OOP 語言(例如 Java、C#)死板的類型約束,讓寫 TS 變得輕松而有趣。
限于篇幅原因,本文不打算詳解所有 TS 的特性。不過,TS 中有很多其他實用的特性,例如聯(lián)合類型、枚舉、泛型、命名空間 等,希望進一步了解或使用 TS 的讀者可以到官方文檔深入了解。
用 TS 構(gòu)建大型項目
對于大型項目,你首先聯(lián)想到的是什么?對于不同領(lǐng)域的開發(fā)工程師來說,可能有不同的理解。不過單就軟件行業(yè)來看,一個軟件開發(fā)項目被稱作 “大型項目”(Large Project),通常意味著將涉及大量的功能模塊,從而具有較高系統(tǒng)復(fù)雜性(Complexity)。一個大型項目的成功,除了滿足傳統(tǒng)項目管理中的周期、預(yù)算控制以外,還需要注重質(zhì)量,從軟件工程角度看來就是可靠性(Robustness)、穩(wěn)定性(Stability)、可維護性(Maintainability)、可擴展性(Scalability)等等。就像建筑工程一樣,你一定不希望自己設(shè)計的建筑是搖搖欲墜的,你需要它盡可能的穩(wěn)固,在狂風暴雨下屹立不倒,同時可以靈活修繕維護。而為了保障這些非功能性需求或者質(zhì)量要求,必須要有一些明確的規(guī)范和標準,在建筑工程里也許是工程圖紙、精密測量等,而在軟件工程里則是代碼規(guī)范(Coding Standard)、設(shè)計模式(Design Patterns)、流程規(guī)范(Process Standard)、系統(tǒng)架構(gòu)(System Architecture)、單元測試(Unit Testing)等等。其中的代碼規(guī)范和設(shè)計模式尤為重要,因為這是整個軟件系統(tǒng)的基礎(chǔ)。沒有代碼,將沒有軟件程序;而沒有好的代碼規(guī)范和設(shè)計模式,將沒有好的代碼,也就沒有可靠的軟件程序,有的只是源源不斷的程序 Bug 和系統(tǒng)崩潰。TypeScript 可以在代碼規(guī)范和設(shè)計模式上極大的提高代碼質(zhì)量,進而增強系統(tǒng)的可靠性、穩(wěn)定性和可維護性。
代碼風格經(jīng)常是前端項目的痛點,這個可以用 JavaScript 風格檢測工具 ESLint 來規(guī)范。但是,ESLint 無法解決類型方面帶來的問題。只有 TypeScript 可以靜態(tài)檢測在類型層面上可能出現(xiàn)的問題。如果用 TS 來編寫前端項目,我會建議對所有可能的變量和方法都用類型約束。而且盡可能不要用 any 類型,因為 any 類型可以涵蓋所有其他類型,相當于沒有類型,這也失去了類型系統(tǒng)的意義。
跟純 JS 相比,TS 雖然多了一些額外的類型聲明代碼,但它卻因此而獲得了更好的可讀性、可理解性和可預(yù)測性,因此也會更加穩(wěn)定和可靠。如果你是從 JS 轉(zhuǎn)到 TS 的初學者,請盡可能克制住不要用 any,這會毀了你的規(guī)范和標準。如果你是前后端分離的架構(gòu)而且在用純 JS 寫前端,在沒有明確且可靠的后端接口數(shù)據(jù)結(jié)構(gòu)約定時,你可能會被迫使寫很多類型判斷的代碼,這跟在靜態(tài)語言中用反射(Reflect)一樣會污染整個代碼風格。
TS 其實就是在 JS 上多了一個靜態(tài)類型系統(tǒng),沒有什么其他的魔法。但就是這個 “小小的” 的附加功能讓代碼變得規(guī)范和可維護,從而成為大型前端項目的首選。很多高級前端工程師都喜歡用 React 或 Angular 來構(gòu)建大型項目,就是因為它們能支持 TS。不過近期發(fā)布的 Vue 3 也及時的加入了 TS 的支持,讓它能夠勝任大型項目的開發(fā)。
在你編寫大型前端項目時,推薦使用聲明文件(Declaration Files)來管理接口或其他自定義類型。聲明文件一般是
以下例子是一個集成了 TS 的 Vue 項目目錄。
.
├──?babel.config.js???//?Babel?編譯配置文件
├──?jest.config.ts???//?單元測試配置文件
├──?package.json????//?項目配置文件
├──?public???????//?公共資源
├──?src?????????//?源代碼目錄
│???├──?App.vue?????//?主應(yīng)用
│???├──?assets?????//?靜態(tài)資源
│???├──?components???//?組件
│???├──?constants????//?常量
│???├──?i18n??????//?國際化
│???├──?interfaces???//?聲明文件目錄
│???│???├──?components??//?組件聲明
│???│???├──?index.d.ts??//?主聲明
│???│???├──?layout???//?布局聲明
│???│???├──?store????//?狀態(tài)管理聲明
│???│???└──?views????//?頁面聲明
│???├──?layouts?????//?布局
│???├──?main.ts?????//?主入口
│???├──?router?????//?路由
│???├──?shims-vue.d.ts?//?兼容?Vue?聲明文件
│???├──?store??????//?狀態(tài)管理
│???├──?styles?????//?CSS/SCSS?樣式
│???├──?test??????//?測試
│???├──?utils??????//?公共方法
│???└──?views??????//?頁面
└──?tsconfig.json????//?TS?配置文件
其中可以看到,interfaces 這個目錄跟其他模塊在同一級,而其子目錄則是其他模塊所對應(yīng)的類型聲明。編寫代碼前,盡量先創(chuàng)建并設(shè)計聲明文件內(nèi)容,設(shè)計好之后再到實際的模塊下完成實現(xiàn)。當然,這個 “定義 -> 實現(xiàn)“ 是一個不斷迭代的過程,可能實現(xiàn)過程中發(fā)現(xiàn)有類型設(shè)計問題,可以回到聲明文件中完善定義,再到實現(xiàn)代碼里進行優(yōu)化。
在 ES6 出來之前,JavaScript 的類型系統(tǒng)是比較難以理解的,主要原因之一就是它難以掌握的原型繼承模式。在 ES6 之前,要實現(xiàn)一個傳統(tǒng) OOP 中的工廠方法,都顯得舉步維艱。不過,ES6 的出現(xiàn)緩解了前端工程師的痛點。ES6 引入了 class 語法糖來實現(xiàn)傳統(tǒng)的 OOP 的類創(chuàng)建與繼承。不過這在 TypeScript 看來,只是 “小打小鬧”。雖然 ES6 中的 class 讓 JS 擁有了一部分封裝的能力,但在構(gòu)建大型項目或系統(tǒng)模塊時還顯得力不從心。缺少類型系統(tǒng),尤其是泛型,是 JS 的一個致命弱點。而 TS 不僅有 ES6 種 class 的所有功能,還有接口、泛型、裝飾器等功能來實現(xiàn)各種靈活而強大的框架級系統(tǒng)。泛型(Generics)通常是框架類庫的必備特性,它可以讓一個類或方法變得更通用。我建議在大型項目中廣泛的使用接口、泛型等特性來抽象你的代碼和邏輯,從而提取出重復(fù)代碼,并在整體上對項目進行優(yōu)化。
如何學習 TS
本文的主要目的是鼓勵開發(fā)者用 TypeScript 構(gòu)建大型前端項目,而并不是一個參考指南。因此,本篇文章不會詳細介紹如何從頭開始創(chuàng)建 TS 項目,或如何運用各種 TS 特性來編寫代碼。在閱讀過本文之后的讀者如果對 TS 有深入研究的興趣,需要閱讀更多 TS 參考文章來深入理解。
對于初學者來說,本文將給出一些途徑來幫助你快速掌握 TS。
官方文檔是最權(quán)威的參考指南,直接從官方文檔上手是最系統(tǒng)也是最有效的學習途徑。
仔細閱讀 TypeScript 官方文檔,包括 Get Started、Hand Book、Tutorials 等等;
如果不習慣閱讀英文文檔,可以去 TypeScript 中文網(wǎng) 查看文檔。
TS 已經(jīng)發(fā)展了很多年,在網(wǎng)上介紹 TS 的技術(shù)文章已經(jīng)汗牛充棟,這里只介紹一些比較有幫助的博客文章。
Typescript 最佳實踐
一份不可多得的 TS 學習指南
結(jié)合實例學習 Typescript
可能是你需要的 React + TypeScript 50 條規(guī)范和經(jīng)驗
“實踐出真理”,在學習過程中,理論和實踐始終是分不開的。如果能將所學知識運用在實際項目中,不僅會加深印象,還可以親自體會新技術(shù)帶來的好處以及它還存在的缺點。你可以嘗試以下實踐項目的途徑。
從頭開始創(chuàng)建一個 TypeScript 項目,用腳手架工具 Vue CLI、create-react-app 等搭建 TS 項目;
學習成熟的 TS 項目,例如 Ant Design、Nest、Angular 等,了解聲明文件的組織方式;
學習開源項目 DefinitelyTyped,理解并實踐如何讓老 JS 項目支持 TS。
總結(jié)
擁抱 TypeScript 是現(xiàn)代前端工程的主流。任何一個前端工程師都需要學習 TS,這有助于拓寬你的知識技能,以及加強自己的專業(yè)度和職業(yè)背景。不過,我們必須意識到,擁抱 TS 并不是終點。如果你研究過前端發(fā)展的歷史,你會發(fā)現(xiàn) TypeScript 的盛行是前端工程化、模塊化和規(guī)模化的結(jié)果,是前端需求變得越來越復(fù)雜的必然產(chǎn)物。TS 只是用后端工程知識解決前端工程問題的一個有效解決方案。TypeScript 之父巧妙的將 C# 的經(jīng)驗移植到了 JavaScript,由此誕生了 TypeScript,革命性的解放了前端工程師的生產(chǎn)力。而這個解決方案會持續(xù)多久,我們無法預(yù)測。但可以確定,前端工程將繼續(xù)發(fā)展,各種新技術(shù)也將曾出不窮。作為一個軟件工程師,你需要不斷學習,這樣才能不被后浪拍死在沙灘上。
TypeScript web前端
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。
版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔相應(yīng)法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。