/

TypeScript 教程

TypeScript 教程

TypeScript 是 2018 年崛起最快的技術之一。它無所不在,人人都在談論它。本文將引導您理解其關鍵概念。

過去幾年中,很少有技術像 TypeScript 一樣具有影響力。

讓我向 TypeScript 提供一些社交證據。

根據《2018 JavaScript 狀態調查》中的調查,近50%的受訪者表示他們使用過 TypeScript,並且將再次使用。超過30%的人表示他們想要學習 TypeScript。這是一個巨大比例的人對它感興趣。

TypeScript 由 Microsoft 開發,對於創建編程語言並不陌生,其中一位創造者是安德斯·海爾斯伯格(Anders Hejlsberg),一位丹麥軟件工程師,以 Turbo Pascal(❤️)和 Delphi 而聞名。我在 Turbo Pascal 旁邊放了一個心形符號,因為 Pascal 是我學習的第一種編程語言,我們在學校使用的是 Turbo Pascal。

它是一種開源語言,公開在 https://github.com/Microsoft/TypeScript 上開發。

Angular 完全基於 TypeScript,據說 Vue.js 將使用 TypeScript 進行第3版開發,Node.js 的創建者 Ryan Dahl 也對它表示了讚賞。

我認為這些事實可以幫助您對 TypeScript 有所了解。它不僅僅是下個月會消失的一種隨機 JavaScript 口味,它肯定是來留下的。隨著事物的發展,這意味著您可能在未來的項目中或下一份工作中需要使用它。也許它還能幫助您找到工作,所以我們就開始吧。

編寫並編譯第一個 TypeScript 文件

開始使用 TypeScript 很簡單。如果您曾經編寫過一行 JavaScript 代碼,那麼您已經編寫過 TypeScript 代碼!

我所說的這一點是 TypeScript 成功的原因之一:它是 JavaScript 的嚴格超集。

這有點像 SCSS 對於 CSS 一樣。

具體而言,它是 ECMAScript 2015(也稱為 ES6)的超集。這意味著任何有效的 JavaScript 代碼都是有效的 TypeScript 代碼。

TypeScript 的許多功能與 JavaScript 的功能相等。例如,變量、模塊系統、迭代器等等。

因此,沒有必要為您的絕對第一個 TypeScript 文件編寫代碼,因為您在不知不覺中可能已經這麼做了,但是我們可以通過明確地創建一個 TypeScript 文件並將其編譯為 JavaScript 來創建一個簡單的 “Hello World” 程序。

運行 npm install -g typescript 以全局安裝 TypeScript 編譯器,然後使用 tsc 命令來使用它。

創建一個新的文件夾,並創建 app.ts 文件,.ts 是 TypeScript 的文件擴展名。

編寫以下第一個程序:

1
2
3
4
5
const greet = () => {
console.log('Hello world!')
}

greet()

這只是一個普通的 JavaScript 程序,但存儲在 .ts 文件中。

現在使用 tsc app.ts 編譯該程序。結果將是一個新的 JavaScript 文件:app.js,其內容如下:

1
2
3
4
var greet = function () {
console.log('Hello world!');
};
greet();

TypeScript 代碼已經編譯為 JavaScript。JavaScript 代碼略有變化,例如您可以注意到它添加了分號,使用了 var 而不是 const,並使用了一個普通函數而不是箭頭函數。

看起來像是 舊的 JavaScript,對吧?這是因為 TypeScript 默認編譯為 ES5,這是幾乎可以保證在所有現代瀏覽器中支持的 ECMAScript 版本。您可以將編譯目標更改為其他版本,例如要編譯為 ES2018,使用以下命令:tsc app.ts --target ES2018

1
2
3
4
const greet = () => {
console.log('Hello world!');
};
greet();

看,與我們原始的 .ts 文件相比,幾乎沒有任何改變,只是添加了額外的分號。

還有一個非常方便的在線 playground,可以讓您玩轉 TypeScript 到 JavaScript 的編譯,網址是 https://www.typescriptlang.org/play/

類型

類型是 TypeScript 的主要功能。

到目前為止,我們編譯了一個 .ts 文件,但我們只是編譯了普通的 JavaScript。

您看到了 TypeScript 的一個功能:您可以使用現代 JavaScript 並將其編譯為 ES5(或更高版本),這有點像 Babel 所做的事情。

我們尚未使用任何 TypeScript 的功能。

TypeScript 提供的最重要的功能是類型系統:靜態類型、接口、類型推斷、枚舉、混合類型、泛型、聯合/交叉類型、訪問修飾符、空值檢查。

如果您曾經使用過像 Go 或 C 這樣的類型語言,您已經知道它是如何運作的。如果不了解,只在動態語言(如 Python 或 Ruby)中編程,那麼這對您來說可能是全新的,但請不要擔心。

類型系統允許您對變量、函數參數和函數返回類型進行類型註釋,從而為您的程序提供更嚴格的結構。

優點是更好的工具支持:編譯器(以及像 VS Code 這樣的編輯器)可以在開發過程中幫助您許多,當您編寫代碼時指示出錯誤。如果沒有類型,這些錯誤是不可能被檢測到的。此外,團隊協作變得更加容易,因為代碼更加明確。

我們編譯後的 JavaScript 代碼當然不含類型:在編譯過程中丟失了它們,但編譯器將指出任何可能發現的錯誤。

以下是如何在 TypeScript 中定義字符串變量:

1
const greeting: string = "hello!"

類型推斷允許我們在明顯的情況下省略類型:

1
const greeting = "hello!"

類型由 TypeScript 確定。

這是函數接受特定類型參數的方式:

1
2
3
const multiply = (a: number, b: number) => {
return a * b
}

如果將字符串傳遞給 multiply() 函數,編譯器將給出錯誤。

以下是函數聲明其返回值的方式:

1
2
3
const multiply = (a: number, b: number): number => {
return a * b
}

有效的類型包括:

  • number
  • string
  • boolean
  • enum
  • void
  • null
  • undefined
  • any
  • never
  • Array
  • tuple

any 是一個綜合型別,可以識別任何類型。

ES2015/ES6 在 JavaScript 中添加了類作為對原型繼承的簡單語法糖。

無論喜歡與否,在 JavaScript 的底層,它仍然使用原型繼承,具有所有獨特的功能和怪癖。

TypeScript 類與 JavaScript 類有稍微不同。原因是 TypeScript 在 JavaScript 擁有類之前引入了類(它們在 ES2015/ES6 中引入)。

與 JavaScript 類相同,您可以以以下方式聲明類:

1
2
3
class Car {

}

這是如何定義類字段的:

1
2
3
class Car {
color: string
}

所有字段默認為公有。您可以將字段設置為私有受保護

1
2
3
4
5
class Car {
public color: string
private name: string
protected brand: string
}

與其他編程語言一樣,private 字段只能在聲明它們的類中訪問。protected 字段只能由相關類訪問。

您還可以聲明靜態字段,它們是類字段而不是對象字段:

1
2
3
class Car {
static numberOfWheels = 4
}

您可以通過構造函數初始化字段:

1
2
3
4
5
6
class Car {
color: string
constructor(theColor: string) {
this.color = theColor
}
}

這種簡化的語法讓事情變得更簡單:

1
2
3
4
5
6
7
8
9
class Car {
constructor(public color: string) {}

printColor() {
alert(this.color)
}
}

(new Car('red')).printColor()

請注意,我們使用 this.x 引用了類字段。

字段也可以是只讀的:

1
2
3
class Car {
readonly color: string
}

在這種情況下,它的值只能在構造函數中設置。

類具有方法:

1
2
3
4
5
6
7
8
9
class Car {
color: string
constructor(public color: string) {
this.color = color
}
drive() {
console.log('You are driving the car')
}
}

就像在普通的 JavaScript 中一樣,您可以使用 new 關鍵字從這些類中創建對象:

1
const myCar = new Car('red')

您可以使用 extend 關鍵字擴展現有的類:

1
2
3
class ElectricCar extends Car {
//...
}

您可以在構造函數和方法中調用 super() 來調用擴展類的對應方法。

存取器

字段可以具有 getter 和 setter。例如:

1
2
3
4
5
6
7
8
9
10
11
class Car {
private _color: string

get color(): string {
return this._color
}

set color(color: string) {
this._color = color
}
}

抽象類

類可以被定義為抽象的,這意味著需要有一個繼承它並實現其抽象方法的類:

1
2
3
4
5
6
7
8
9
abstract class Car {
abstract drive()
}

class SportsCar extends Car {
drive() {
console.log('You are driving a sports car')
}
}

接口

接口建立在基本類型之上。您可以將接口用作類型,並且此接口可以包含其他類型定義:

1
2
3
4
5
6
7
8
9
10
interface SetOfNumbers {
a: number;
b: number;
}

const multiply = (set: SetOfNumbers) => {
return set.a \* set.b
}

multiply({ a:1, b: 2 })

接口也可以是類實現的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Car {
name: 'string'
new (brand: string)
drive(): void
}

class SportsCar implements Car {
public name
construtor(public brand: string) {
//...
}
drive() {
console.log('You are driving a sports car')
}
}

函數功能

函數可以使用 ? 符號在參數名後面加上可選參數:

1
2
3
4
5
6
7
8
9
class Car {
drive(kilometers?: number) {
if (kilometers) {
console.log(`Drive the car for ${kilometers} kilometers`)
} else {
console.log(`Drive the car`)
}
}
}

參數也可以具有默認值:

1
2
3
4
5
class Car {
drive(kilometers = 10) {
console.log(`Drive the car for ${kilometers} kilometers`)
}
}

函數可以透過使用 rest 參數來接受不同數量的參數:

1
2
3
4
5
6
7
class Car {
drive(kilometers = 10, ...occupants: string[]) {
console.log(`Drive the car for ${kilometers} kilometers, with those people on it:`)
occupants.map((person) => console.log(person))
}
}
(new Car()).drive(20, 'Flavio', 'Roger', 'Syd')

枚舉類型

枚舉是一種定義命名常量的很好方式,這很遺憾在 JavaScript 中並不受支持,但在其他語言中有很流行。

TypeScript 為我們提供了枚舉:

1
2
3
4
5
6
enum Order {
First,
Second,
Third,
Fourth
}

TS 在內部為每個值分配了唯一的標識符,我們可以引用 Order.FirstOrder.Second 等。

您可以明確地為常量分配值:

1
2
3
4
5
6
enum Order {
First = 0,
Second = 1,
Third = 2,
Fourth = 3
}

或者也可以使用字符串:

1
2
3
4
5
6
enum Order {
First = 'FIRST',
Second = 'SECOND',
Third = 'THIRD',
Fourth = 'FOURTH'
}

泛型

泛型是許多不同編程語言的一個特性。簡而言之,您可以創建一個函數、接口或類,這些函數、接口或類可以使用不同類型,而不需要在起始時指定類型。

但在編譯時,如果您開始使用具體的類型,然後將類型更改為另一個類型(例如從 number 變為 string),編譯器將拋出一個錯誤。

我們可以通過省略類型或使用 any 來完成此操作,但是使用泛型,所有工具都將能夠幫助我們。

示例語法:

1
2
3
4
function greet<T>(a: T) {
console.log(`Hi ${a}!`)
}
greet('Flavio')

有趣的 T 符號標識了一個泛型類型。

該類型可以限制為特定的類族或接口,使用 extends 關鍵字:

1
2
3
4
5
interface Greetable { name: string }
function greet<T extends Greetable>(a: T) {
alert(`Hi ${a.name}!`)
}
greet({ name: 'Flavio'})

我們已經準備好了更多內容!

這是 TypeScript 的基礎知識。請繼續閱讀官方文檔來了解詳細信息,或開始編寫您自己的應用程序,並在實際操作中學習!