天天看點

一文教你如何學習TypeScript

在看這篇文章之前,我是強烈推薦​​TypeScript 入門教程​​這本書的。因為這本書它是:從 JavaScript 程式員的角度總結思考,循序漸進的了解TypeScript。文章來源也是該書,但聽我一句話:踩過坑才代表會了。【建議收藏】

希望你看這本書之前已經具備了以下技能:

  • 熟練使用JavaScript開發日常項目

一、什麼是TypeScript?

一文教你如何學習TypeScript

1、TypeScript詞解

Typed JavaScript at Any Scale。

添加了類型系統的 JavaScript,适用于任何規模的項目。

以上是官網​​[1]​​對于 TypeScript 的定義。強調了兩個重要特性:

  1. 類型系統
  2. 适用于任何規模

2、TypeScript的特性

1、類型系統

一文教你如何學習TypeScript

從 TypeScript 的名字就可以看出來,「類型」是其最核心的特性。

我們知道,JavaScript 是一門非常靈活的程式設計語言(弱類型):

  • 沒有類型限制,一個變量可能開始是字元串,過一會兒又被指派為數字。
  • 隐式轉換可以在運作中把變量類型轉來轉去。
  • 基于原型的面向對象程式設計,使得原型上的屬性或方法可以在運作時被修改。
  • 函數是 JavaScript 中的一等公民,可以指派給變量,也可以當作參數或傳回值。

這種靈活性就像一把雙刃劍,一方面使得 JavaScript 蓬勃發展,無所不能(App,桌面、小程式、網站、各種架構、服務端);但在另一方面卻因代碼參差不齊導緻維護成本較高。

而 TypeScript 的類型系統,在很大程度上彌補了 JavaScript 的缺點。

TypeScript 是靜态類型

類型系統按照「類型檢查的時機」來分類,可以分為動态類型和靜态類型。

動态類型是指在運作時才會進行類型檢查,這種語言的類型錯誤往往會導緻運作時錯誤。JavaScript 是一門解釋型語言,沒有編譯階段,是以它是動态類型,以下這段代碼在運作時才會報錯:

let foo = 1;
foo.split(' ');
// Uncaught TypeError: foo.split is not a function
// 運作時會報錯(foo.split 不是一個函數),造成線上 bug      

靜态類型是指編譯階段就能确定每個變量的類型,這種語言的類型錯誤往往會導緻文法錯誤。TypeScript 在運作前需要先編譯為 JavaScript,而在編譯階段就會進行類型檢查,是以 TypeScript 是靜态類型,這段 TypeScript 代碼在編譯階段就會報錯了:

let foo = 1;
foo.split(' ');
// Property 'split' does not exist on type 'number'.
// 編譯時會報錯(數字沒有 split 方法),無法通過編譯      
一文教你如何學習TypeScript

這段 TypeScript 代碼看上去和 JavaScript 沒有什麼差別。

實際上大部分 JavaScript 代碼都隻需要經過少量的修改(或者完全不用修改)就能變成 TypeScript 代碼,這得益于 TypeScript 強大的「類型推論」,即使不去手動聲明變量 ​

​foo​

​​ 的類型,也能在變量初始化時自動推論出它是一個 ​

​number​

​ 類型。

完整的 TypeScript 代碼是這樣的:

let foo: number = 1;
foo.split(' ');
// Property 'split' does not exist on type 'number'.
// 編譯時會報錯(數字沒有 split 方法),無法通過編譯      

TypeScript 是弱類型

類型系統按照「是否允許隐式類型轉換」來分類,可以分為強類型和弱類型。

以下這段代碼不管是在 JavaScript 中還是在 TypeScript 中都是可以正常運作的,運作時數字 ​

​1​

​​ 會被隐式類型轉換為字元串 ​

​'1'​

​​,加号 ​

​+​

​​ 被識别為字元串拼接,是以列印出結果是字元串 ​

​'11'​

​。

console.log(1 + '1');
// 列印出字元串 '11'      

TypeScript 是完全相容 JavaScript 的,它不會修改 JavaScript 運作時的特性,是以它們都是弱類型。

作為對比,Python 是強類型,以下代碼會在運作時報錯:

print(1 + '1')
# TypeError: unsupported operand type(s) for +: 'int' and 'str'      

若要修複該錯誤,需要進行強制類型轉換:

print(str(1) + '1')
# 列印出字元串 '11'      

JavaScript 和 TypeScript 中不管加号兩側是什麼類型,都可以通過隐式類型轉換計算出一個結果——而不是報錯——是以 JavaScript 和 TypeScript 都是弱類型。

雖然TypeScript是弱類型,但我們可以通過TypeScript提供的類型系統,以及ESLint提供的代碼檢查功能。使得TypeScript更像是一個「強類型」。

這樣的類型系統展現了 TypeScript 的核心設計理念:在完整保留 JavaScript 運作時行為的基礎上,通過引入靜态類型系統來提高代碼的可維護性,減少可能出現的 bug。

2、适用于任何規模

TypeScript 非常适用于大型項目,類型系統可以為大型項目帶來更高的可維護性,以及更少的 bug。

在中小型項目中由于有[類型推論],大部分類型都不需要手動聲明了,是以不會很大程度的降低開發效率,故部分中小企業也會選擇它。

TypeScript 還可以和 JavaScript 共存。如果你是老的JavaScript項目,你想改造成TypeScript的項目,但又代碼量太大并且項目要繼續更新。那麼你可以新的内容和檔案用TypeScript,老的不變。當閑下來時,可以逐漸将老的一個一個檔案的更新為TypeScript。

事實上,就算你從來沒學習過 TypeScript,你也可能已經在不知不覺中使用到了 TypeScript——在 VSCode 編輯器中編寫 JavaScript 時,代碼補全和接口提示等功能就是通過 TypeScript Language Service 實作的:

一文教你如何學習TypeScript

一些第三方庫原生支援了 TypeScript,在使用時就能獲得代碼補全了,比如 Vue 3.0:

一文教你如何學習TypeScript

有一些第三方庫原生不支援 TypeScript,但是可以通過安裝社群維護的類型聲明庫(比如通過運作 ​

​npm install --save-dev @types/react​

​ 來安裝 React 的類型聲明庫)來獲得代碼補全能力——不管是在 JavaScript 項目中還是在 TypeScript 中項目中都是支援的:

一文教你如何學習TypeScript

由此可見,TypeScript 的發展已經深入到前端社群的方方面面了,任何規模的項目都或多或少得到了 TypeScript 的支援。

3、與标準同步發展

TypeScript 的另一個重要的特性就是堅持與 ECMAScript 标準同步發展。

ECMAScript 是 JavaScript 核心文法的标準,自 2015 年起,每年都會釋出一個新版本,包含一些新的文法。

一個新的文法從提案到變成正式标準,需要經曆以下幾個階段:

  • Stage 0:展示階段,僅僅是提出了讨論、想法,尚未正式提案。
  • Stage 1:征求意見階段,提供抽象的 API 描述,讨論可行性,關鍵算法等。
  • Stage 2:草案階段,使用正式的規範語言精确描述其文法和語義。
  • Stage 3:候選人階段,文法的設計工作已完成,需要浏覽器、Node.js 等環境支援,搜集使用者的回報。
  • Stage 4:定案階段,已準備好将其添加到正式的 ECMAScript 标準中。

一個文法進入到 Stage 3 階段後,TypeScript 就會實作它。一方面,讓我們可以盡早的使用到最新的文法,幫助它進入到下一個階段;另一方面,處于 Stage 3 階段的文法已經比較穩定了,基本不會有文法的變更,這使得我們能夠放心的使用它。

除了實作 ECMAScript 标準之外,TypeScript 團隊也推進了諸多文法提案,比如可選鍊操作符(​

​?.​

​​)、空值合并操作符(​

​??​

​)、Throw 表達式、正則比對索引等。

3、怎麼安裝TypeScript和編輯器推薦

1、安裝 TypeScript

TypeScript 的指令行工具安裝方法如下:

npm install -g typescript
# 或者用yarn      

以上指令會在全局環境下安裝 ​

​tsc​

​​ 指令,安裝完成之後,我們就可以在任何地方執行 ​

​tsc​

​ 指令了。

編譯一個 TypeScript 檔案很簡單:

tsc hello.ts      

我們約定使用 TypeScript 編寫的檔案以 ​

​.ts​

​​ 為字尾,用 TypeScript 編寫 React 時,以 ​

​.tsx​

​ 為字尾。

2、編輯器

TypeScript 最大的優勢之一便是增強了編輯器和 IDE 的功能,包括代碼補全、接口提示、跳轉到定義、重構等。

主流的編輯器都支援 TypeScript,這裡我推薦使用 ​​Visual Studio Code(前端開發神器)​​。

它是一款開源,跨終端的輕量級編輯器,内置了對 TypeScript 的支援。無需要編譯階段,寫完就能看到對不對。

一文教你如何學習TypeScript

[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-6acs5DXl-1635497989842)(<> “點選并拖拽以移動”)]​​

上圖寫完就報了錯誤,因為number沒有length屬性。

另外vscode本身也是用 TypeScript 編寫的。

下載下傳安裝:​​Visual Studio Code - Code Editing. Redefined​​

4、第一個案例Hello TS

我們從一個簡單的例子開始。

将以下代碼複制到 ​

​hello.ts​

​ 中:

function sayHello(person: string) {
    return 'Hello, ' + person;
}

let user = 'TS';
console.log(sayHello(user));      

然後執行

tsc hello.ts      

這時候會生成一個編譯好的檔案 ​

​hello.js​

​:

function sayHello(person) {
    return 'Hello, ' + person;
}
var user = 'TS';
console.log(sayHello(user));      

在 TypeScript 中,我們使用 ​

​:​

​​ 指定變量的類型,​

​:​

​ 的前後有沒有空格都可以。

上述例子中,我們用 ​

​:​

​​ 指定 ​

​person​

​​ 參數類型為 ​

​string​

​。但是編譯為 js 之後,并沒有什麼檢查的代碼被插入進來。

這是因為 TypeScript 隻會在編譯時對類型進行靜态檢查,如果發現有錯誤,編譯(ts檔案轉換成js的過程)的時候就會報錯。而在運作時,與普通的 JavaScript 檔案一樣,不會對類型進行檢查。

如果我們需要保證運作時的參數類型,還是得手動對類型進行判斷:

function sayHello(person: string) {
    if (typeof person === 'string') {
        return 'Hello, ' + person;
    } else {
        throw new Error('person is not a string');
    }
}

let user = 'TS';
console.log(sayHello(user));      

下面嘗試把這段代碼編譯一下:

function sayHello(person: string) {
    return 'Hello, ' + person;
}

let user = [0, 1, 2];
console.log(sayHello(user));      

編輯器中會提示錯誤,編譯的時候也會出錯:

一文教你如何學習TypeScript

但是還是生成了 js 檔案:

function sayHello(person) {
    return 'Hello, ' + person;
}
var user = [0, 1, 2];
console.log(sayHello(user));      

這是因為 TypeScript 編譯的時候即使報錯了,還是會生成編譯結果,我們仍然可以使用這個編譯之後的檔案。

如果要在報錯的時候終止 js 檔案的生成,可以在 ​

​tsconfig.json​

​​ 中配置 ​

​noEmitOnError​

​​ 即可。關于 ​

​tsconfig.json​

​​,請參閱​​官方手冊​​​(​​中文版​​)。 後面會在TypeScript基礎的原始資料類型[數值]中說明sconfig.json怎麼來的!

二、TypeScript基礎

1、原始資料類型

​​原始資料類型 · TypeScript​​

PS:空值中:

與 ​

​void​

​​ 的差別是,​

​undefined​

​​ 和 ​

​null​

​​ 是所有類型的子類型。也就是說 ​

​undefined​

​​ 類型的變量,可以指派給 ​

​number​

​ 類型的變量:(需要tsconfig.json中strict設定為false)

2、任意值

​​任意值 · TypeScript​​

3、類型推論

​​類型推論 · TypeScript​​

4、聯合類型

​​聯合類型 · TypeScript​​

PS:需要注意的是,一旦定義了可選屬性,那麼同一個接口内其他所有的屬性,都隻能是可選屬性的子集,否則将會報錯 !集合概念:string | number 為例,number為它的子集,string也為它的子集。

另一個版本問題:

一文教你如何學習TypeScript
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-dRdWMx1b-1635497989845)(<> “點選并拖拽以移動”)]​

5、對象的類型—接口

​​對象的類型——接口 · TypeScript​​

6、數組的類型

​​數組的類型 · TypeScript​​

PS:疑惑? {

    [index: number]: number;

    length: number;

    callee: Function;

}

中任意類型為number為什麼 callee可以是Function?

7、函數的類型

​​函數的類型 · TypeScript​​

8、類型斷言

​​類型斷言 · TypeScript​​

9、聲明檔案

​​聲明檔案 · TypeScript​​

10、内置對象

​​内置對象 · TypeScript​​

三、TypeScript進階

​​進階 · TypeScript​​

四、總結

咳,偷個懶,不一一列舉。還是那句話,對于​​TypeScript 入門教程​​​這本書我是極其推薦的,學 TypeScript 還是得看它。除了以上的内容外,它還囊括了​​工程化配置​​說明。我以我學弟的人格擔保,看它必會!沖啊xdm!

繼續閱讀