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 陸續再加入改良。

TypeScript 1.6

  • 支援 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)
  • 支援泛型別別名

TypeScript 1.7

  • 支援 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()

Type Script 1.8

  • 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 安裝新版。


Comments

Be the first to post a comment

Post a comment