TypeScript 真的值得嗎?

做者:Paul Cowan

翻譯:瘋狂的技術宅javascript

原文:https://blog.logrocket.com/is...前端

未經容許嚴禁轉載java

在開始以前,但願你們知道,我是 TypeScript 愛好者。它是我在前端 React 項目和基於後端 Node 工做時的主要編程語言。但我確實有一些疑惑,因此想在本文中進行討論。迄今爲止,我已經用 TypeScript 寫了至少三年的代碼,因此 TypeScript 作得的確不錯,並且知足了個人需求。ios

TypeScript 克服了一些很難解決的問題,併成爲前端編程領域的主流。 TypeScript 在這篇列出了最受歡迎的編程語言的文章中排名第七位。git

不管是否使用 TypeScript,任何規模的開發團隊都應該遵循如下慣例:程序員

  • 編寫良好的單元測試——應在合理範圍內涵蓋儘量多的生產代碼
  • 結對編程——額外的審視能夠捕捉到的錯誤遠遠超過語法錯誤
  • 良好的同行評審流程——正確的同行評審能夠檢查出許多機器沒法捕獲的錯誤
  • 使用 linter,例如 eslint

TypeScript 能夠在這些基礎之上增長額外的安全性,但我認爲這在編程語言需求列表中應該排在後面。github

TypeScript 不是健全的類型系統

我認爲這多是 TypeScript 當前版本的主要問題,可是首先讓我定義 健全非健全 的類型系統。面試

健全性

健全的類型系統是可以確保你的程序不會進入無效狀態的系統。例如,若是表達式中的靜態類型爲 string,則在運行時,要保證在評估它時僅得到 stringtypescript

在健全的類型系統中,絕對不會在編譯時或運行時產生表達式與預期類型不匹配的狀況。編程

固然 TypeScript 有必定程度的健全性,並捕獲如下類型錯誤:

// 'string' 類型不可分配給 'number' 類型
const increment = (i: number): number => { return i + "1"; }

// Argument of type '"98765432"' is not assignable to parameter of type .
// 沒法將參數類型 '"98765432"' 分配給參數類型'number'。
const countdown: number = increment("98765432");

不健全

100% 的健全性不是 Typescript 的目標,這是在 non-goals of 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 在健全和不健全的陣營中腳踏兩隻船。這種半途而廢的現象是經過 any 類型強制執行的,我將在後面提到。

我仍然須要編寫不少的測試,這讓我感到沮喪。當我第一次開始使用 TypeScript 時錯誤地得出結論:能夠沒必要編寫這麼多單元測試了。

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

我可以理解爲何 TypesScript 會走這條路,而且有一個論點指出,若是健全類型系統可以獲得 100% 的保證,那麼對 TypeScript 的使用率講不會那麼高。這種觀點隨着 dart 語言的逐漸流行( Flutter 現已被普遍使用)被反駁了。健全性是 dart 語言的目標,這裏是相關的討論(https://dart.dev/guides/langu...)。

不健全以及 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,但這可能意味着你必須複製本身的model。

可怕的 any 類型和嚴格性選項

any 類型就是這樣,編譯器容許任何操做或賦值。

TypeScript 在一些小細節上每每很好用,可是人們傾向於在 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,若是咱們輸入了錯誤的內容,將會得到很好的視覺反饋。

image.png
vscode中的TypeScript錯誤

經過 TypeScript 還能夠加強重構的功能,而且在對修改後的代碼進行編譯時,能夠當即識別出代碼的改變(例如方法簽名的更改)。

TypeScript 啓用了良好的類型檢查,而且絕對要比沒有類型檢查或僅使用普通的 eslint 更好,可是我認爲它還能夠作更多的事情。對於那些想要更多的人來講,還可以提供足夠多的編譯器選項。


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索