天天看點

TypeScript 被吹過頭了?

雲栖号資訊:【 點選檢視更多行業資訊

在這裡您可以找到不同行業的第一手的上雲資訊,還在等什麼,快來!

開始看本文之前,我希望讀者朋友們知道我在很大程度上是一位 TypeScript 粉絲。在我的前端 React 項目和各種後端 Node 工作裡,所使用的主要程式設計語言都是 TypeScript。我是這條船上的人,但也确實有一些疑惑,想在這篇文章中讨論一下。到目前為止,我已經使用 TypeScript 寫了至少三年的代碼,涉及的項目不計其數,是以可以說 TypeScript 的确是走在了正路上,或者說滿足了某種需求。

TypeScript 克服了一些難以逾越的障礙,成為了前端程式設計領域的主流之選。TypeScript 在這篇文章列出的最受歡迎程式設計語言中排名第七。

無論你是否在使用 TypeScript,不管多大規模的軟體團隊都應遵循以下實踐:

  • 精心編寫的單元測試應在合理範圍内覆寫盡可能多的生産代碼;
  • 結對程式設計——另一雙眼睛可以捕捉到的遠不止文法錯誤;
  • 良好的同行評審流程——正确的同行評審可以找出許多機器無法捕獲的錯誤;
  • 使用 eslint 之類的 linter。

TypeScript 可以在這些基礎之上增加額外的一層安全性,但我認為這在程式設計語言的需求清單中是排在非常靠後的位置上的。

TypeScript 并非健全的類型系統

我認為這可能是 TypeScript 目前版本面臨的主要問題,但首先,我來定義一下什麼是健全(sound)和不健全(unsound)的類型系統。

健全

一個健全的類型系統能確定你的程式不會進入無效狀态。例如,如果一個表達式的靜态類型是 string,則在運作時,對它求值時你肯定隻會得到一個 string。

在健全的類型系統中,永遠不會在編譯時 或運作時 出現表達式與預期類型不比對的情況。

當然,健全性(soundness) 也是有級别的,具體含義可以斟酌。TypeScript 具有一定程度的健全性,并會捕獲以下類型的錯誤:

// 類型 'string' 不能指派給 類型 'number'
const increment = (i: number): number => { return i + "1"; }

// 類型 '"98765432"' 的參數不能指派給類型 'number' 的參數.
const countdown: number = increment("98765432");           

不健全

關于 Typescript,以下事實是非常明确的:100%的健全性不是它的目标,并且 TypeScript 的非目标清單中的 3 号非目标明确指出:

應用一個健全或“正确無誤”的類型系統(不是我們的目标)。相反,要在正确性和生産力之間取得平衡。

這意味着不能保證變量在運作時具有定義的類型。我可以用下面一些人為的例子說明這一點:

interface A {
    x: number;
}

let a: A = {x: 3}
let b: {x: number | string} = a;
b.x = "unsound";
let x: number = a.x; // 不健全

a.x.toFixed(0); // 這啥?           

上面的代碼是不正确的,因為從 A 接口中可知 a.x 應該是一個數字。不幸的是,經過一些重新指派後,它最終以一個字元串的形式出現,并且後面的代碼能通過編譯,但會在運作時出錯。很不幸,這裡顯示的表達式可以正确編譯:

a.x.toFixed(0);           

我認為這可能是 TypeScript 的最大問題,那就是健全性不是它的目标。我仍會遇到許多運作時錯誤,這些錯誤沒有被 tsc 編譯器标記出來,但如果 TypeScript 有一個健全的系統就能做到了。采用這種方法,意味着 TypeScript 在健全和不健全的陣營之間是在腳踩兩隻船。這種搖擺路線遇上 any 類型就更嚴重了,我将在後文提到這一點。我還是得編寫盡可能多的測試,這讓我感到沮喪不已。當我第一次開始使用 TypeScript 時,我錯誤地得出了一個結論,以為以後就用不着編寫這麼多單元測試了。

TypeScript 挑戰了現狀,聲稱降低使用者使用類型的認知開銷比類型健全更重要。

我了解為什麼 TypesScript 會走這條路,并且有一個觀點指出,如果類型系統的健全性得到 100%保證,那麼 TypeScript 的采用率就不會那麼高了。随着 dart 語言逐漸流行(因為 Flutter 現已廣泛應用),這個看法也被證僞了。健全性是 dart 語言的一個目标,可以參考這裡的讨論。

TypeScript 的不健全性,以及它在嚴格的類型系統上開的一大堆天窗拖累了它的效率,讓它在今天處于相當 雞肋 的位置上,非常可惜。我的願望是,随着 TypeScript 的流行,會有更多的編譯器選項可供使用,進而使進階使用者可以争取 100%的健全性。

TypeScript 不保證任何運作時類型檢查

運作時類型檢查不是 TypeScript 的目标之一,是以這種願望可能永遠不會實作。例如,在處理從 API 調用傳回的 JSON 負載時,運作時類型檢查會很有用。如果我們可以在類型級别上控制它,就用不着一整筐錯誤和許多單元測試了。

我們無法在運作時保證任何事情,是以可能會出現以下情況:

const getFullName = async (): string => {
  const person: AxiosResponse = await api();

  //response.name.fullName 可能在運作時成為 undefined
  return response.name.fullName
}           

有一些支援這種需求的庫,例如 io-ts;雖然它很棒,但可能意味着你必須複制你的模型。

可怕的 any 類型和嚴格選項

any 類型就是字面意思,編譯器允許任何操作或指派。

TypeScript 往往在小事上表現很不錯,但是人們習慣給花費時間超過 1 分鐘的任何事物都來個 any 類型。我最近在一個 Angular 項目中工作,看到很多這樣的代碼:

export class Person {
 public _id: any;
 public name: any;
 public icon: any;           

TypeScript 會讓你忘掉類型系統。你可以用一個 any 把所有事物的類型幹掉:

("oh my goodness" as any).ToFixed(1); // 記得我提到健全性時說的話嗎?           

strict 編譯器選項會啟用以下編譯器設定,這些設定會讓事物看起來更健全:

  • –strictNullChecks
  • –noImplicitAny
  • –noImplicitThis
  • –alwaysStrict

還有 eslint 規則 @typescript-eslint/no-explicit-any。

any 的擴散會毀掉你類型系統的健全性。

結論

我必須重申一點,我是 TypeScript 粉絲,也在日常工作中使用它,但我确實認為它有很多缺陷,而且炒作有些過頭了。Airbnb 聲稱 TypeScript 可以阻止 38%的錯誤。我非常懷疑這一精确的百分比數字。TypeScript 不會給已有的良好實踐錦上添花。我還是得編寫盡可能多的測試。你可能不相信,我編寫的代碼變多了,可能還得編寫許多類型測試。我仍然會遇到意外的運作時錯誤。

TypeScript 提供了比較基礎的類型檢查,但健全性和運作時類型檢查不是它的目标,這讓 TypeScript 處于腳踩兩條船的狀态,一隻腳踏上了更好的那艘,一隻腳還呆在現在的破船上,真是不幸。

TypeScript 的亮點在于良好的 IDE 支援,比如說 vscode 裡如果我們打錯什麼内容,就會獲得視覺回報。

TypeScript 被吹過頭了?

vscode 中的 TypeScript 錯誤

使用 TypeScript 還可以增強重構能力,并且在對修改後的代碼運作 TypeScript 編譯器時,可以立即識别出代碼中斷(例如方法簽名的更改)。

TypeScript 帶來了良好的類型檢查,并且絕對比沒有類型檢查器或僅使用普通的 eslint 要更好,但是我認為它能更進一步。另外對于我們這種想要更多能力的使用者來說,它應該可以提供足夠多的編譯器選項才是。

英文原文

Is TypeScript Worth It?

【雲栖号線上課堂】每天都有産品技術專家分享!

課程位址:

https://yqh.aliyun.com/zhibo

立即加入社群,與專家面對面,及時了解課程最新動态!

【雲栖号線上課堂 社群】

https://c.tb.cn/F3.Z8gvnK

原文釋出時間:2020-03-24

本文作者:Paul Cowan

本文來自:“

InfoQ

”,了解相關資訊可以關注“

繼續閱讀