原文地址:learn-typescript-in-30-minutes
原文作者:Danny Markov
譯者:Jerry
譯文首發地址:jerryjiao.life/
今天我們來學習 Typescript,一門被設計用來開發大型和複雜 apps 的語言。相較於鬆散的,弱型別的 Javascript , Typescript 繼承了很多其它高階語言( c# , java )的概念,進而更加的規範。
這篇入門針對於有一定的 Javascript 基礎並且打算學習 Typescript 的人。我們覆蓋了很多基礎和關鍵的概念,並輔以很多例子來幫助你理解這門語言。讓我們開始吧。
(譯者說明:這篇文章不算新,16年底的文章。但是我瀏覽了其它一些英文的 Typescript 入門的文章,感覺還是這篇文章覆蓋的內容全面一些,更適合初學者。而且一些基礎內容,是變化不大的,所以選擇了這篇文章。翻譯這篇文章的時候,一些2.0新特性的簡述的內容沒有翻譯,因為已經是釋出過的了。)
使用 Typescript 的好處
Javascript 已經很好了,我真的需要學習 Typescript 麼?嚴格意義上講,你不需要通過學習 Typescript 去變成一個好的程式設計師,大部分人沒有使用 Typescript 依然很優秀。當然,使用 Typescript 程式設計也有一些顯而易見的好處:- 因為 Typescript 型別是固定的,用 Typescript 寫的程式碼更加容易把控,也更易於除錯。
- 得益於模組,名稱空間和更好的物件導向程式設計的支援,使得 Typescript 更容易去構建大型和複雜的 apps。
- 因為 Typescript 有編譯成 Javascript 的過程,使其可以在專案上線執行和發生錯誤之前可以捕獲到不同型別的錯誤。
- Angular2 是用 Typescript 寫的,它也推薦開發人員使用 Typescript 去開發專案
最後也很主要的一點,對於大部分人去學 Typescript。 Angular 2 是現在一個很火的程式語言,儘管開發者可以繼續使用 Javascript 去開發,但是很多教程和例子都是用 Typescript 寫的。隨著 Angular 2 社群的擴充套件,自然越來越多的人會選擇 Typescript. (譯者注:這篇教程寫自2016年,現在react,vue,babel都支援了 Typescript。這篇文章中的 Typescript 的基礎概念對現在還是適用的)
(近期 Typescript 普及度增加,資料來自谷歌趨勢)Typescript的安裝
最簡單安裝 Typescript 的方法是通過 npm 工具,使用以下的命令我們可以全域性安裝 Typescript,可以在我們的專案中使用 Typescript 轉換器:npm install -g typescript
複製程式碼
在命令提示行中輸入 tsc -v
可以檢查 Typescript 是否被安裝:
tsc -v
Version 1.8.10
複製程式碼
支援 Typescript 的編輯器
Typescript 是由微軟支援和維護的一個開源專案,所以微軟的Visual Studio原生支援 Typescript。如今,有很多編輯器和IDE都可以原生的或者通過外掛來支援 Typescript 的語法提示,自動補充建議,錯誤提醒甚至編譯 Typescript。
- Visual Studio Code - 微軟的輕量級的開源的編輯器。內建了對 Typescript 的支援
- Sublime Text的官方免費外掛
- 最新版本的 WebStorm
- 更多Vim,Atom,Emac和其它的支援
Typescript的編譯
Typescript一般會寫在.ts(或者.tsx對於jsx來說)檔案中,.ts檔案不能直接用在瀏覽器中,需要先被轉義成.js檔案。Typescript可以通過以下幾種方式編譯:
- 通過前文關注過的,命令列工具
tsc
- 直接在Visual Studio中或者其它IDE中
- 使用一些前端打包工具例如
gulp
我們發現第一種方式更加簡單也對初學者更友好,所以在這篇文章中我們將會使用第一種方式。
下面的命令將會轉換main.ts這個Typescript檔案為main.js.如果main.js存在,它將會被覆蓋。
tsc main.ts
複製程式碼
我們也可以羅列多個檔案或者使用萬用字元去編譯:
# 將會編譯出分離的Javascript檔案: main.js worker.js
tsc main.ts worker.ts
# 編譯所有的在這個目錄下的所有ts檔案,不能遞迴執行。
tsc *.ts
複製程式碼
我們也可以使用--watch來自動編譯Typescript檔案在Typescript檔案改變時:
# 初始化一個watcher的程式來保持main.js為最新
tsc main.ts --watch
複製程式碼
更多Typescript的使用者也使用建立 tsconfig.json, 包含不同的編譯的配置。配置檔案對一個包含很多的.ts檔案的專案編譯很便利,因為它定義了自動化的流程。你可以獲得更多包含tsconfig.json的資訊在這個文件中。
靜態型別
Typescript一個非常明顯的特徵就是支援靜態的型別。這就意味著,在宣告變數的時候要同時宣告變數的型別,編譯器會保證變數不會被賦值為錯誤的型別。一旦變數的型別被宣告,他們將會從你的程式碼中做到自動的推測。
這裡是一個例子,任何的變數,函式以及返回值在一開始都要被宣告型別:
var burger: string = 'hamburger', //String
calories:number = 300, // Numberic
tasty: boolean = true; //Boolean
// 自然地,你也可以 省略變數的宣告
// var burger = 'hamburger'
// 函式一般返回一個string或數字
// 如果函式為void型別,它將不會返回什麼東西
function speak(food:string, energy:number):void {
console.log("Our " + food + " has " + energy + " calories.");
}
speak(burger, calories);
複製程式碼
正因為Typescript將會被轉為js, 一些沒有語義和型別的字元,將會被移除:
// 一個Typescript轉為js的例子
var burger = 'hamburger',
calories = 300,
tasty = true;
function speak(food, energy) {
console.log("Our " + food + " has " + energy + " calories.");
}
speak(burger, calories);
複製程式碼
當然,如果你的程式碼不符合規則,tsc命令列工具將會警告我們錯誤在哪,比如:
// 當變數被宣告為boolean,但被賦值為string的時候
var tasty: boolean = "I haven't tried it yet";
複製程式碼
main.ts(1,5): error TS2322: Type 'string' is not assignable to type 'boolean'.
複製程式碼
如果我們寫錯函式的入參型別,tsc也會提示:
function speak(food: string, energy: number): void{
console.log("Our " + food + " has " + energy + " calories.");
}
// 函式入參的型別錯誤
speak("tripple cheesburger", "a ton of");
複製程式碼
main.ts(5,30): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
複製程式碼
以下是一些最常用的資料型別:
- Number - 所有的數字型別都可以被宣告為number 型別,它們不會被區分為int,float或者其它
- String - 字串型別,和Javascript字串型別一樣,可以被單引號和雙引號包括
- Boolean - true或者false,如果使用0或1將會報錯
- Any - 這種變數它可以被賦值為各種型別,無論是string,number,或者其它各種型別。
- Arrays - 有兩種可能的語法:my_arr:number[]; 或者 my_arr: Array
- Void - 運用在不返回任何值的函式
官方文件羅列了所有Typescript包含的資料型別
介面
介面被用來檢查一個物件(object)是否符合固定的結構。通過定義介面,我們可以命名一個特殊的變數的組合,確保它們的是固定的。當轉譯為Javascript的時候,介面將會消失-介面的目標就是在開發過程中給予幫助。
下面的例子,我們定義一個簡單的介面對函式的入參進行型別檢查:
// 這裡我們定義了一個 Food的介面,包含它的屬性和其型別
interface Food {
name: string;
calories:number;
}
// 我們可以告訴函式我們希望的入參,來符合Food這個介面
// 這種方式可以保證屬性永遠的可用
function speak(food: Food): void {
console.log("Our " + food.name + " has " + food.calories + " calories.");
}
// 我們定義一個物件,它滿足Food介面規定的屬性
// 注意 這個型別將被自動的推測
var ice_cream = {
name: "ice cream",
calories: 200
}
speak(ice_cream);
複製程式碼
屬性的順序不重要,我們要保證屬性名字和型別的正確。如果少了某些屬性,或者寫了錯誤的型別,或者名字不同,ts的編譯器都會警告我們。
interface Food {
name: string;
calories: number;
}
function speak(food: Food): void{
console.log("Our " + food.name + " has " + food.calories + " grams.");
}
// 我們寫了一個錯誤的屬性名 nmae
var ice_cream = {
nmae: "ice cream",
calories: 200
}
speak(ice_cream);
複製程式碼
報錯資訊:
main.ts(16,7): error TS2345: Argument of type '{ nmae: string; calories: number; }
is not assignable to parameter of type 'Food'.
Property 'name' is missing in type '{ nmae: string; calories: number; }'.
複製程式碼
這是一個面對初學者的教程,如果你想獲得更多關於interface的資訊,請訪問ts的官方文件
型別和麵向物件程式設計
當構建一個大型的apps的時候,大多數開發者喜歡物件導向程式設計,尤其是在java,c#這樣的程式語言裡。Typescript提供了類的系統,它很像Java,c#這樣的語言。包含繼承,抽象類,介面實現,setters/getters和其它。我們同樣也關注到最新Javascript的更新(ECMAScript 2015),Javascript原生對class提供了支援。Typescript和Javascript對類的應用很相似,但是有不同的地方。Typescript更嚴格一些。
延續之前food例子的風格,下面是一個簡單的Typescript類的例子:
class Menu {
// 類中的屬性:
// 預設的,它們是public的,也可以是private 或者 protected
items: Array<string>; // 在選單中的專案,一個strings型別的陣列
pages: number; // 選單有多少頁面,一個數字
// 建構函式
constructor(item_list: Array<string>, total_pages: number) {
// 這裡的關鍵詞是強制的
this.items = item_list;
this.pages = total_pages;
}
// 方法
list(): void {
console.log("Our menu for today:");
for(var i=0; i<this.items.length; i++) {
console.log(this.items[i]);
}
}
}
// 建立一個新的‘選單’類例項
var sundayMenu = new Menu(["pancakes","waffles","orange juice"], 1);
// 執行list方法
sundayMenu.list();
複製程式碼
熟悉java或c#的程式設計師對這種語法應該感覺很熟悉,下面是繼承的例子:
class HappyMeal extends Menu {
// 父類屬性將會被繼承
// 需要定義一個新的建構函式
constructor(item_list: Array<string>, total_pages: number) {
// 在這個例子中,我們希望和父類有同樣的建構函式
// 為了更方便額度複製父類的建構函式,這裡使用了super函式-引用了父類的建構函式
super(item_list, total_pages);
}
// 和屬性一樣,父類函式也會被繼承
// 當然我們也可以重寫list()函式覆蓋父類中的方法
list(): void{
console.log("Our special menu for children:");
for(var i=0; i<this.items.length; i++) {
console.log(this.items[i]);
}
}
}
// Create a new instance of the HappyMeal class.
var menu_for_children = new HappyMeal(["candy","drink","toy"], 1);
// This time the log message will begin with the special introduction.
menu_for_children.list();
複製程式碼
如果想更深入的瞭解Typescript類的內容,請閱讀官方文件
泛型
泛型是一個模板,它允許函式接受不同型別的入參。相較於使用any這個型別,用泛型建立一個複用的元件會更好,因為泛型保留了進出變數的型別。
下面是一個簡單的例子,這個函式會接收一個引數並且返回包含這個引數的陣列。
// 在函式後面的<T>識別符號表示了一個函式為泛型函式
// 當我們執行函式的時候,每一個T的例項將會被替換為真實提供的型別
// 接收一個入參,型別為T
// 返回一個陣列,型別為T
function genericFunc<T>(argument: T): T[] {
var arrayOfT: T[] = []; //建立一個T型別的空陣列
arrayOfT.push(argument); // 入參新增進這個陣列中
return arrayOfT;
}
var arrayFromString = genericFunc<string>("beep");
console.log(arrayFromString[0]); // "beep"
console.log(typeof arrayFromString[0]) // String
var arrayFromNumber = genericFunc(42);
console.log(arrayFromNumber[0]); // 42
console.log(typeof arrayFromNumber[0]) // number
複製程式碼
第一次我們執行函式的時候,我們不需要手動的傳入的引數的型別(string),因為編譯器可以識別到傳遞了什麼引數,並自動決定什麼型別最適合它;就像在第二個呼叫中那樣,雖然這不是必須的,但是還是推薦每次都指定泛型的型別,因為編譯器可能在複雜的情景下推測出錯誤的型別。
Typescript文件中包含了兩個高階的例子,包含了泛型類,以及與介面結合使用的方法。如果想閱讀更多,請點選這裡。
模組
另一個關於大型apps的概念是模組化。如果你的程式碼可以劃分為許多小的,可重複使用的模組,將會使你的專案更加條理和易於理解的。比一個有10000行的檔案好很多。
Typescript引入了一種語法去輸出和引入模組,但是無法掌控檔案間的實際連線。為了能使Typescript檔案使用第三方的庫,例如用在瀏覽器apps的require.js和Node.js的CommmonJs.讓我們看一個簡單的例子--Typescript模組和require.js:
我們有兩個檔案,一個輸出函式,另一個引入它並且執行它:
expoter.ts
var sayHi = function():void {
console.log("Hello!");
}
export = sayHi;
複製程式碼
importer.ts
import sayHi = require('./exporter');
sayHi();
複製程式碼
現在我們需要下載require.js並用 <script>
標籤引入到檔案中 -
這裡會告訴你怎麼做。最後一個是轉換這兩個.ts檔案。在轉換的時候輸入另一個引數,告訴編譯器我們想使用AMD的方式引用,而不是 CommonJS 的方式
tsc --module amd *.ts
複製程式碼
如果你想了解更多Typescript模組的內容可以點選這裡。
宣告檔案
當我們想使用之前Javascript中經常使用的庫,我們需要去使用宣告檔案去使得這些庫適用於ts。一個宣告檔案的副檔名為.d.ts,包含了關於庫的資訊和API。ts宣告檔案經常是手寫的,但是一些庫也有其它人已經寫好的.d.ts檔案。DefinitelyTyped是最大的公共資源倉庫,包含了很多的檔案可引用。另一個著名的用node模組管理Typescript定義的叫Typings.
更多關於宣告檔案的內容可以看這裡。
更多內容
剛開始,閱讀官方文件的內容可能有點多,但是帶來的收益是巨大的。我們的教程只是做一個入門的介紹,我們不可能覆蓋所有文件的內容。下面是我們沒有包含到的內容:
最後
希望這篇入門教程能給你帶來幫助。 如果你有任何想法或者想使用Typescript在你的專案裡,可以在下面留言。