前言

這系列標題不是 Angular 2之 30天邁向神乎奇蹟之路嗎?怎麼才第二天標題就是 TypeScript(之後簡稱 TS)了呢?怎麼有種掛羊頭賣狗肉的感覺呢?XD 先前提到 Angular 2很重要的一環便是它採用 TS當作開發語言,如果 Angular是一個網頁前端的精靈,那 TS就是他的靈魂啦!
事實上雖然你也可以完全使用純 JavaScript(之後簡稱JS)來開發,因為 TS本身就完全相容 JS,但身為 Super版的 JS,大家都在用了(尤其老大哥微軟跟 Google),尚未成為神乎其技的我們,跟著強者的方法一起修道準沒錯。所以首先我們要先把 TS弄清楚,不管是語法、變數、函數、風格等等之類的,都要有所了解,這樣對於之後開發 Angular只會有益無害,更何況學習新的事情是多麼令人開心的事呢!

TypeScript

接著就來認識一下 TS是甚麼吧!

緣起

TS是為了解決 JS諸多的問題,例如資料型別(typing)、名稱空間(namescpace)以及零碎的眉眉角角,而且由於當初設計 JS的階段過於倉促,加上沒有先例可以參考(第一個同時兼具函數程式和物件導向程式的語言),總總原因讓大家對 JS不太滿意,於是就有很多改善 JS的語言誕生,它們都希望減輕 JS開發人員的負擔,改用一些結構良好或是更輕鬆的語言來開發應用程式(Script#、CoffeeScript),再透過各自的編譯器來產出 JS程式碼, TS也是其中一個。

語法

接著就來快速入門吧!讓我們看看 TypeScript有那些特殊的地方。

const & let

嚴格說起來這不算是 TS特殊的東西,在 ES6中對於宣告變數得更明確規範。

const cannotChange = 9; // 只要改變這個常數,程式會出錯
let canChange = [{'a': '1'}, {'b': '2'}]; //在不改變宣告的資料型別的情況下,可以改變這個參數

var雖然還是能用,但能不用盡量不要用,越明確的規範對程式幫助越好。

資料型別(typing)

以往在 JS 宣告變數是不需要宣告型別的,var 就是萬能,物件可以變數字,字串可以變布林,在絕大多數的情況下,這樣變來變去其實不好,雖然說不用宣告型別很方便,但就算再細心還是有出錯的一天,所以 TS又強制規定要宣告型別了。
以下是所有型別範例:

//Number 
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

//Stirng
let person: string = "Mike"; //可以用 ""
let age: number = 37;
let sentence: string = Oh, ${person} is ${age} years old.; //也可以用 ${}
//上面等於 "Oh, " + person + " is " + age + " years old."

//Array
let list: number[] = [1, 2, 3];

//Tuple
let x: [string, number]; // Array中包含不同型別的變數用 Tuple
x = ["hello", 10]; // OK
x = [10, "hello"]; // Error
x[3] = true // Error 往後的變數只能是一開始設定的 string 或 number

型別註釋(Type annotations)

以往在 JS中函數的參數是不需要宣告參數的型別。

JS:
function greeter(person) {
    return "Hello, " + person;
}

但是這樣沒人知道 person是甚麼,到底是物件還是字串?

TS:
function greeter(person: string) {
    return "Hello, " + person;
}

所以 TS強制要求參數要註釋型別 person: string這樣一看就知道是字串,當專案越做越大,函數呼叫函數又在呼叫下個函數,參數滿天飛的時候,才不會搞錯資料型別,又 debug半天才發現是資料給錯!
註釋也可以放入指定型態的物件,就是接下來的 Interface。

Interfaces

interface 讓參數為物件時,有更明確的結構型態,而不是只是丟個參數表示為一個物件。
簡單的範例應該可以讓大家懂 interface 的概念。

interface Person {
    firstName: string;
    lastName: string;
}

function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}

var user = { firstName: "Tiger", lastName: "Liu" };

console.log(greeter(user));

類別(Classes)

以往在 ES5 的 JS 是沒辦法像 C++、JAVA那樣直接宣告類別的,JS開發者當然還是能透過一些技巧達到類似效果,但就比較麻煩也很不直觀。而 ES6 版本之後 JS 加入類別的語法糖,TS 中就能用相同的方式宣告類別,編譯成 JS後其實就是以前我們用來模擬類別的技巧啦!XD

class Student {
    fullName: string;
    constructor(public firstName, public middleInitial, public lastName) {
        this.fullName = `${firstName} ${middleInitial} ${lastName}`;
    }
}

interface Person {
firstName: string;
lastName: string;
}

function greeter(person : Person) {
return Hello, ${person.firstName} ${person.lastName};
}

let user = new Student("Jane", "M.", "User");

console.log(greeter(user));

靜態成員

類別的靜態成員,他的屬性存在於類本身,而不是類的實例上

class Human {
    static hands: number = 2;
    static legs: number = 2;
}

繼承

有類別的概念,當然就會有繼承的概念

class Woman extends Human {
    static gender: string = 'female';
}

Interface 進階

interface 也可以用來強制類別符合約束

interface Shape {
    area(): number;
}

class Circle implements Shape {
radius: number;
constructor(radius: number) {
this.radius = radius;
}
area(): number {
return this.radius * this.radius * 3.1415;
}
}

interface 也可以繼承其他的 interface

interface Shape {
    area(): number;
}

interface Color {
RGB: string;
}

interface Thing extends Color, Shape {
}

迭代

多了 let ... of ... 的用法

let list = [4, 5, 6];

for (let i in list) {
console.log(i); // "0", "1", "2",
}

for (let i of list) {
console.log(i); // "4", "5", "6"
}

let ... of ... 的操作其實長這樣

let list = [4, 5, 6];
for (let _i = 0; _i < list.length; _i++) {
    var num = list[_i];
    console.log(num);
}