TypeScript 2.0 已於 9/22 正式推出,想起從1.4版起已好久沒有深入了解改版差異,順勢做個重點整理。
TypeScript 改版歷程
TypeScript 1.0 推出時,由於具有支援強型別、介面、繼承等物件導向語言特性,提供編譯期錯誤檢查,再加上完整 IDE 支援,很適合開發大型且複雜的 JavaScript 程式,獲得許多前端開發者青睞,TypeScript 也自此成為我開發網站的首選。
TypeScript 在 1.1 時選擇重寫編譯器,提供四倍速度,同時也移上 Github,鼔勵開源社群參與協作。
1.4 TypeScript 加入大量 ES2015/ES6 支援以及新特性,包含:
- Union Types ★★★
例如: x: number | number[],x 的型別可以是數字或是數字陣列 - Type Alias
為型別取別名,例如:type NgScope = ng.IScope, type Callback = () => void - Const Enums
編譯時直接置換為數字,在 JavaScript 端完全隱形 - 支援 ES6 的 Let/Const
let 的用法與 var 相同,差別在於 let 嚴格限定變數存活範圍,杜絕干擾區塊外變數的可能性。const 則可用於宣告後定義好就不容更改的變數。 - Template String ★★★★★
直接看範例:
var rectangle = { height: 20, width: 10 };
var areaMessage = `First line,
Rectangle area is ${rectangle.height * rectangle.width}`;
樣版字串使用「`」符號取代單引號或雙引號,在字串可嵌入變數,還像C# @"…"可支援換行,串接HTML標籤接到落落長時格外好用,大推!
延伸閱讀:TypeScript Template Strings
TypeScript 1.5陸續加入更多 ES6 支援,包含:
- ES6 Module概念
在 ES6 習慣以模組概念拆解成多個程式碼檔案,使用 export 決定對外公開範圍,要用時以 import 匯入引用。前幾天初試 Angular 2已體驗過這種新做法。 - Destructing
使用陣列形式快速指定變數值
const iterable = ['a', 'b'];
var [x, y] = iterable; // x = 'a'; y = 'b' - Spread運算子 ★★
用法 function drawText(x: number, y: number, ...strings: string[]) { … }
概念相當於 C# 的 params,例如:void DrawText(decimal x, decimal y, params string[] strings)
故寫成 drawText(10,20,"hello") drawText(10,20,"hello","world")都通,但Spread運算子還可加於呼叫參數前方,例如:
var pos=[10,20], strings=["one","two"];
drawText(…pos, "hello",…strings,"world");
等同drawText(10,20,"hello","one","two","world"); - for … of 語法
類似 C# foreach (var … in …):
for (var v of expr) { }
等同於
for (var _i = 0, _a = expr; _i < _a.length; _i++) {
var v = _a[_i];
} - 支援ES6內建Symbol 參考
- ES6 Computed Property
- Module 輸出選擇
除了 AMD、CommonJS 外,再新增 SystemJS、UMD - 支援使用 tsconfig.json 設定專案以及編譯選項
- Decorator
與 Angular、Ember、Aurelia 開發團隊合作,TypeScript 1.5 融入 ES7 的 Decorator 特性,它也是 Angular 2 開發的重要關鍵:(如以下程式的 @Component 及 @View )import {Component, View, NgFor, bootstrap} from “angular2/angular2”;
import {loadFile} from “audioFile”;
import {displayAudioFile} from “displayAudio”;
@Component({selector: ‘file-list’})
@View({template: `
<select id=”fileSelect” size=”5″>
<option *ng-for=”#item of items; #i = index”
[selected]=”selected === item”(click)=”updateSelection()”>{{ item }}</option>
</select>`,
directives: [NgFor]
})
class MyDisplay {
items: string[];
constructor() {
this.items = [“item1”, “item2”];
}
updateSelection() { … }
}
TypeScript 1.6-1.8 陸續再加入改良。
- 支援 React Typing 及 JSX
- Class Expression
一列寫完類別宣告,例如:
class StateHandler extends class { reset() { return true; } } { - 自訂型別檢核
例如以下程式,若 a 是 Dog,在編譯時期就會出錯
function isCat(a: Animal): a is Cat {
return a.name === ‘kitty’;
} - Intersection Type
在 JavaScript 裡有時會使用 Mixin 概念或 jQuery.extended()方法融合兩個不同型別物件同時具備兩種型別的介面,過去在這種情況下要實現強型別,需宣告一個新介面或類別以兼容兩種型別的介面,1.6 起可用 T & U 代表融合 T 與 U 屬性方法的混合型別,例如:
function extend<T, U>(first: T, second: U): T & U - 支援抽象類別(Abstract Class)
- 支援泛型別別名
- 支援 async / await
- Polymorphic this Typing
多形概念的 this,主要用於 Fluent 串接式 API,看範例比較好懂:interface Model {
setupBase(): this;
}
interface AdvancedModel extends Model {
setupAdvanced(): this;
}
declare function createModel(): AdvancedModel;
newModel = newModel.setupBase().setupAdvanced(); // fluent style works
- ES6 Module Emitting
新增 module 參數,面對 Node.js v4 不支援 ES6 模組但支援 ES6 特性的情境,可以 target 參數設 es6,但模組用 commonjs。 - ES7 Exponentiation
ES7 規格,let cubed = 2 ** 3 –> cubed = 2 * 2 * 2,取代 Math.pow()
- Module Augmentation
允許 import Module 後再擴充其介面加入新屬性、方法 - String Literal Types ★★★
限定字串變數只能使用列舉的字串值,例如: easing: "ease-in" | "ease-out" | "ease-in-out"; 若 easing = "out" 會出現Error: Type '"out"' is not assignable to type '"ease-in" | "ease-out" | "ease-in-out"' - 流程分析更加智慧化
例如:return 後馬上換行陷阱偵測,未 return 等同傳回 undefined 警告。
至於 TypeScript 2.0,台灣 MSDN 部落格有篇文章有 Beta 版的詳細介紹,這裡簡單條列:
- --strictNullChecks 限定 string、number 等型別變數不允許被設為 null 或 undefined,除非使用 string | null 或 string[] | undefined,如必要可加上「!」排除變數為 null 或 undefined 情況以避開編譯錯誤。例如:
let lowerCased = strs!.map(s => s.toLowerCase()); - 編譯器能更精準掌握變數在某段程式碼位置時是什麼型別
- 模組宣告簡化
在 TS1.x 為描述外部程式庫,我們可能需要寫成以下宣告
declare module "foo" {
var x: any;
export = x;
}
在 TS2.0 只要寫成 declare module "foo" 即可
2.0 RC加入的改良如下:
- Tagged Unions ★★★
Tagged Unions(又稱為 Discriminated Unions, Disjoint Unions, 或 Algebraic Data Types)已是 F#, Swift, Rust, JavaScript 常用的設計模式。用個例子來說明,Circle 與 Square 都有 kind 屬性,但在 Circle 寫死為 "circle",在 Square 則為 "square":interface Circle {
kind: "circle";
radius: number;
}
interface Square {
kind: "square";
sideLength: number;
}
type Shape = Circle | Square;
在 TS1.8 裡,取得 Shape 聯集型別(Union Type)後必須轉型成 Circle 才能取得半徑,轉成 Square 才能讀取邊長:function getArea(shape: Shape) {
switch (shape.kind) {
case"circle":
// Convert from 'Shape' to 'Circle'
let c = shape as Circle;
return Math.PI * c.radius ** 2;
case"square":
// Convert from 'Shape' to 'Square'
let sq = shape as Square;
return sq.sideLength ** 2;
}
}
TS2.0 變聰明了,知道何時 Shape 是 Circle,何時是 Square:function getArea(shape: Shape) {
switch (shape.kind) {
case"circle":
// 'shape' is a 'Circle' here.
return Math.PI * shape.radius ** 2;
case"square":
// 'shape' is a 'Square' here.
return shape.sideLength ** 2;
}
}
- 1.8 推出的 String Literal Type 廣受好評,2.0 再擴大到 boolean, number ★★★
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
let nums = Digit[];
nums.push(16); <= 出錯,因為16不在列舉範圍內 - tsconfig.json 支援萬用字元
例如:{
"include": [
"./src/**/*.ts"
],
"exclude": [
"./src/tests/**"
]
}
其中:*代表0或多個非分隔符號字元、? 代表一個非分隔符號字元、**/ 代表任意層的子目錄
再補上幾條 TypeScript 2.0 革新:
- 簡化宣告檔(d.ts)的引用程序
在 VS Code 等應用中,使用 npm install –s @types/lodash 安裝 Scoped Package後,在專案中遇到需要 lodash 定義時將自動找到適合的版本下載。 - 增加 readonly 修飾字
用法與 C# readonly 相同,只允許由建構式指定值,之後不得再變動。
註:標上 ★★★ 的是我覺得特別好用的功能,黑大嚴選,大力推薦!
要使用 TypeScript 2.0,VS2015 必須更新到 Update 3,並安裝 TypeScript 2.0 for Visual Studio 2015;Visual Studio Code 則可透過 npm install –g typescript@2.0 安裝新版。