- Published on
TypeScript 10分鐘快速入門
- Authors
- Name
- Eddy Chang
TypeScript 是一個以 JavaScript 語言為基礎,所延伸出來的另一種超集的程式語言,也就是說 TypeScript 涵蓋了幾乎目前 ECMAScript 標準的新式語言範圍,並額外加了許多關於資料類型的預先定義與相互檢查部份。學習 TypeScript 需要有對新式的 ECMAScript 的一定熟悉程度才能相得益彰、共取其利,不然只是讓你的學習之路備感艱難。
TypeScript 是為了解決什麼問題而誕生的?
這個問題的由來是因為 JavaScript 是一種動態資料類型的程式語言,動態資料類型代表在程式碼中,變數會自動依照指定值變更資料類型,這是直譯式腳本語言的常見特性,有容易學習與使用的優點,但也是非常大的缺點。類型的使用與轉換,常常都是程式碼中潛在的問題,例如開發者經常會因為指定值的類型錯誤,造成不可預期的結果,或是有些函式庫在使用時,如果沒有仔細觀看文件,甚至或是文件寫得不清不楚,也容易造成資料類型誤用的情況。
TypeScript 加入靜態資料類型在原本的 JavaScript 後,代表可以讓變數或資料定義時,就可以指定必需使用哪一種資料類型,之後在使用時如果發生資料類型不相符時,就會發出預先的警告,這對於開發具有一定規模化的 JavaScript 應用程式時,更具有強健性相當有幫助。
註:在這篇部落格文章:Flow 靜態資料類型的檢查工具,10 分鐘快速入門中可以參考 Flow 工具與 TypeScript 的比較。
環境安裝 & 第一次的編譯體驗
一開始學習 TypeScript,你只需要下面兩個東西,不需要花太多時間建立環境,因為你的首要重點是在於學習它是什麼,和能幫你作什麼而已:
Node 安裝好後,裡面會包含 npm 套件管理程式,請打開命令列工具(終端機)視窗輸入以下的指令:
npm install -g typescript
安裝成功後,你可以在命令列中輸入tsc
,應該會看到目前的 TypeScript 編譯器的版本和各種資訊,像下面這樣:
❯ tsc
Version 3.9.7
//...
試著打開 Visual Studio Code 或其它你撰寫 JavaScript 的工具程式,然後貼上以下的程式碼,存成一個test.ts
檔案:
function foo(name: string) {
return 'Hello ' + name
}
foo(123)
然後將命令列工具的指令,切換到這個檔案的目錄下,然後輸入tsc
指令來進行編譯,像下面這樣:
❯ tsc test.ts
接著可以看到 TypeScript 編譯器報錯的訊息,這個錯誤是說123
與我們要傳入foo
所需的參數資料類型不符:
❯ tsc test.ts
test.ts:5:5 - error TS2345: Argument of type '123' is not assignable to parameter of type 'string'.
5 foo(123)
~~~
Found 1 error.
當然,手動編譯是一件無趣的事情,我們在實際專案開發時也不會這樣來作這件事情。
這裡只是要說明,TypeScript 的檔案都是以.ts
為副檔名,最終編譯為.js
檔案,理所當然的,它在編譯的過程中就會執行它的檢查工作,尤其是對類型有使用上的問題,它是回報錯誤,但還是會讓你編譯成功,最後還是會產生一個test.js
的檔案。
如果你想要在編譯時,如果有錯誤發生就不要產出.js
檔,你可以用下面的指令,也就是多加了--noEmitOnError
:
tsc --noEmitOnError test.ts
這裡我要先說明的是,TypeScript 的編譯器與設定選項比你想像中的內容還多了幾十倍,相互的設定關聯也複雜了幾十倍。初學入門花太多時間在這上面,會有點浪費時間而且也沒多大意思,我們主要學習的是它能作什麼,和該怎麼作,然後是不是在你要作的事情上能協助這些事情。這些設定選項我們可以一開始先用別人設定好的,和幾個常用到的,其它的細部設定,等你哪天有需要再回頭來查詢即可。
由簡單的範例開始說明
我們回頭看一下剛剛上面的程式碼:
function foo(name: string) {
return 'Hello ' + name
}
foo(123)
唯一與你學過的 JavaScript 不同之處,就是foo
函式的傳入參數後面多了一個:string
的指示詞,這東西在 TypeScript 中稱為"Type Annotation/A 諾貼遜/",中文是"類型註解"的意思。
這意思是說,這個傳入參數我們希望它是會個 string(字串)類型,不可以是別的資料類型。所以在foo(123)
這個函式呼叫或執行時,TypeScript 就幫我們抓出了錯誤,因為是傳入參數的資料類型錯誤。
對於函式而言,實際在 TypeScript 中要撰寫函式需要養成習慣,函式會有傳入參數和回傳值的兩種情況,像上述的範例,應該要寫成像下面這樣:
function foo(name: string): string {
return 'Hello ' + name
}
foo(123)
上面這代表foo
的回傳值也是一個string
類型,如果函式沒有回傳值的話,你可以用void
這個 TypeScript 為函式專門設計的沒有回傳值用的類型,實際上 JavaScript 的函式就算你沒寫,也是會回傳undefined
就是了。
當然傳入或回傳的類型註解有可能會有很多種,或是還有傳入參數預設值的寫法,所以我們的函式就會寫得複雜得多了,像下面這樣的程式碼範例:
function foo(name: string | number = 'Eddy'): string {
if (typeof name === 'number') return 'Hello Somebody'
return 'Hello ' + name
}
foo('Bob') // Hello Bob
foo(123) // Hello Somebody
foo() //Hello Eddy
上面可以看到我們的傳入參數的類型註解變得複雜多了,像這樣(name : string | number = 'Eddy')
,意思是可以是number
或string
,然後預設值是Eddy
字串。
聯集類型(Union/尤里恩/ Types)是用這個符號豎線(|),也就是或(or)的意思,另外還有一個用符號和號(&),稱為交叉類型(Intersection/英特謝遜/ Types),也就是與(and)的意思,通常會用在物件的類型定義時。(官網文件(英文)或舊文件(簡中))
上面只是簡單的介紹而已,有關於函式類型的詳細內容,可以再參考 TypeScript 官網手冊中,有關函式的章節(官網文件(英文);舊文件(簡中))。
介面(Interface)
對於物件中屬性的類型定義,顯得更複雜得多了,這是學習的重點中的重點,在這上面的學問相當的多,細節也很多。TypeScript 使用了介面來描述物件的外形(Shape)樣子,以下面的範例程式碼來說明:
interface User {
name: string
id: number
username?: string
}
const user1: User = {
name: 'Eddy',
id: 0,
}
const user2: User = {
name: 'Jobs',
username: 'jobs123',
id: 0,
}
要注意的是interface
的宣告是一行一個屬性定義,後面並沒有逗號(,),而是要加分號(;)或不加都可以,這只是撰寫風格的差異而已(要不要用分號作語句結尾)。像下面這樣的程式碼範例:
interface User {
name: string
id: number
username?: string
}
或
interface User {
name: string
id: number
username?: string
}
上面可以看到username?
這個屬性後面加了一個符號問號(?),代表它是可選的(optional),也就是"可有可無的"的意思。
介面與物件的內容比你想像也多很多,它們有很多與其它像類型、函式間的交互應用,可以參考官網文件。
類別
ES6 開始讓 JavaScript 有了類別(class)的定義方式,但內容非常的陽春,之後的一直到 ES 這幾年的標準中,加入了不少新的有關於類別的新特性內容。
TypeScript 對於 ES 中的新特性都可以支援得很好,也有自己擴充的有關於類型定義的特性,簡單來說它讓類別整個的定義更為完整,例如用於定義成員的 public、private 與 protected 修飾字等等,以及像是 static(靜態)屬性或 abstract(抽象)類別等等。一個簡單的類別範例,像下面這樣的範例程式碼:
class User {
private name: string
constructor(name: string) {
this.name = name
}
greet() {
console.log(`Hello, ${this.name}!`)
}
}
const eddy = new User('Eddy')
eddy.name // Error: 'name' is private
eddy.greet()
新的 ES2019 (ES10)中加入了 Private fields(私有欄位),長得就不太像上面那樣,而是像下面的範例程式碼,TypeScript 也是支援的:
class User {
#name: string
constructor(name: string) {
this.#name = name
}
greet() {
console.log(`Hello, ${this.#name}!`)
}
}
const eddy = new User('Eddy')
eddy.name // Error: 'name' is private
eddy.greet()
上面兩個範例的差異在於,TypeScript 可以編譯第一個程式碼,也就是自己的 private 為 ES5 標準的 JavaScript,但第二個則是只能編譯為 ES2015 後的版本,所以還是有些差異的部份,太新的 ES 標準真要在編譯為 ES5 標準的 JavaScript 時,或許 babel 才是一個正確的選擇。
結語
最後,我們對 TypeScript 的認識只是剛開始而已,你可以把它當作是 JavaScript 語言的升級版本,或是擴充版本,TypeScript 是為了要能符合規模化應用程式開發,所必要學習的另一條道路。
就上面的內容中所提及的,TypeScript 的內容遠比你想像的的多,官方的文件提供了很好的參考資料,但並不是整個都要看完學完,才是真正學會它。學習基本的,然後其它在需要的時候來查閱,這才是學習的正確方式。