TypeScript -- Angular 2 的寫作靈魂
- 2016-12-17
- Liu, An-Chi 劉安齊
前言
這系列標題不是 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);
}