TypeScript新手村

去年因為工作的關係,開始接觸 TypeScript,從後端跳過來前端的同事表示沒什麼學習難度,但我剛碰的時候覺得十分有障礙,不習慣強型別之外,對於繼承什麼的毫無概念,動不動就報錯編譯失敗,十分苦手啊!這篇小小紀錄一下我學習 TypeScript 的心得。

宣告型別

在宣告變數時也要同時聲明型別,一旦聲明為特定型別之後就不能賦予其他型別的值,不然就會報錯。

1
2
3
4
5
6
7
let isFail: boolean = false;
isFail = 566 //報錯

let memberName: string = ''
let orderArray: number\[\] = \[1, 2, 3\];
等同於
let orderArray: Array<number> = \[1,2,3\];

問題來了 如果我的陣列裡面同時想要存數字跟字串呢?

1
let mutiple `: (string|number) = [ 1, "message" ];`

那如果我的變數真的需要一開始是字串後來要塞數字呢?

其實一開始宣告變數時,沒有賦值預設就是 any(任意型別)

1
2
3
let content = "";
//等同於
let content: any = "";

這樣就會跟原本的 javascipt 一樣 想怎麼塞就怎麼塞
也可以乖乖的寫成 let myFavoriteNumber: string | number;

class 類別

有接觸過 es6 的人應該對 class 不陌生,以前如果要實作繼承的話,必須透過 function + prototype 來達成,但 es6 提供了 class 這個一種語法糖,並不是真的是以類別為基礎(class-based)的物件導向,實際上仍然是以原型為基礎(prototype-based)的物件導向,另外,class 的名稱須採用大駝峰(ClassName)的寫法,但實測全部小寫也不會編譯不過,但還是要遵循規範,乖乖用駝峰式囉。

先宣告一個 computer 的 class, 定義了一個 name 的屬性和 rebooting 的方法,我會把他想像成是張設計圖,new 一個新的 instance(透過設計圖施工)才會真正的創建出實例物件。

1
2
3
4
5
6
7
8
9
class Computer{
public name: string=""
constructor(name){
this.name=name
}
public rebooting(){
console.log(\`your ${this.name} will rebooting in a minute\`);
}
}

這裡面的 this 是指向物件本身(new 出來的物件)

建構方法 constructor()

constructor 是類別實體化時調用的方法,就像是依照設計稿正式打造出實體本身的方法,我可以不寫 contstructor 嗎?答案是可以,程式碼執行時會默默幫你加上 constructor。

透過建構子傳入參數,就能建立不同的實體物件(instance)

1
2
3
4
5
6
7
let NB = new Computer("Notbook")
let DeskTop = new Computer(‘DeskTop’)
NB.rebooting()
// 輸出 your Notbook will rebooting in a minute

DeskTop.rebooting()
// 輸出 your DeskTop will rebooting in a minute

inheritance 繼承

利用 extends 來繼承 class,會在 constructor 呼叫 super 方法 => 執行父類別的 constructor,可以透過 super 來取用父層的方法或屬性 。那一定要寫 super 嗎?答案是肯定的,沒調用 super 方法會編譯失敗之外,想要取 this.name 的值也會取不到,因為此時的 this 不知道要指向誰,只有在執行完 super 之後,this 才會指向物件本身。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

class Computer{
public name: string=""
constructor(name){
this.name=name
}
public rebooting(){
console.log(\`your ${this.name} will rebooting in a minute\`);
}
}

class Notbook extends Computer{
constructor(){
super("Notbook")
}
public rebooting(){
super.rebooting() //執行父類別方法
console.log(\`NB rebooting\`);//再執行自己要額外做的程式
}
}

enum 列舉

一開始看到這個第一個反應是物件的一種嗎 XD,比較像是在一種情境下彙整幾種可能的狀況或是整理了一些項目,方便控管也比較一目暸然。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
enum payWay ={creditCard,paypal,cash,linePay}

//初始的 index 跟陣列一樣是從 0 開始

console.log(payWay.creditCard)//輸出 0
console.log(payWay.paypal)//輸出 1
console.log(payWay.cash)//輸出 2
console.log(payWay.linePay)//輸出 3

//也可以手動給值

enum payWay ={creditCard=100,paypal=200,cash=300,linePay=400}

//這樣就可以因應不同的情境,執行各個 function

switch(way){
case payWay.paypal:
//do paypal next step
break;
case payWay.cash:
//do cash next step
break;
}

參數型別

function 的參數也可以宣告型別了,只要丟進來的參數不是預設型別就會報錯,也可以用「?」來設定非必填參數

1
2
3
4
5
function createItem(color: string, item: string, slogan?:string){
return I have ${color} ${item}!!! ${slogan}
}
createItem(123,"pen") 報錯 第一個參數應該要是字串

void

第一次看到這個單字,拿去 google 得到虛空兩個字的解釋,其實就是代表這個 function 不會 return 任何值。

1
2
3
function returnNothing(): void {
console.log("Nothing");
}

public、 private 、protected

類別裡面的屬性都是預設為 public,是什麼意思呢?就是可以在外部取用,private 是無法在外部取用的,protected 可以透過子類別的方法來取到父類別宣告的 protected 變數,我覺得整體的感覺很像臉書的貼文隱私權設定,哪些是公開,哪些是不公開這樣。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//父類別

class Person{
age: number=100
public name: string
public height: number=180
private id: number=147896523
protected gender: string=’unknown’
constructor(name){
this.name=name
}
public walking(){
console.log(\'${this.name} walking in the rain\');
}
}

//子類別

class Tom extends Person{
constructor(){
super("Tom")
}
public getGender(){
console.log(\'my gender is ${this.gender}\')
}
}

let personHeight = new Person("Marry").height //可以取用
let personID = new Person("Marry").personID //報錯 不允許外部取用
let personGender = new Person("Marry").gender //報錯 僅限定子類別取用

Static 靜態屬性

直接定義在類別身上 不需要實例化就可以取用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Cat{
static voice: string = "meow"
constructor(){}
static sayHi(){
console.log(\'${this.voice}~${this.voice}~ ^\_^\')
}
}
//注意,如果静态方法包含 this 关键字,这个 this 指的是类,而不是实例。
class FatCat extends Cat{

}

let blackcat = new Cat()
blackcat.sayHi()//會報錯

Cat.sayHi()// 輸出 meow ~ meow ~^\_^

// 父類別 Cat 有一个静態方法,子類別 FatCat 可以調用這個方法。
FatCat.sayHi()

Interface

好比是學校規定學生每天進校門口前要收集 30 片楓葉才放行,不管你是用手撿,用掃把掃,用吸塵器吸都隨便你,只要做到這件事情即可

介面列舉有哪些規範類需實作 ,但要怎麼實作並不管。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface animals{
category: string;
legs?: number; //問號代表不一定要實作
readonly id: number; //唯讀 創建後不可改變其值
move():void
}
// 搭配 implements(實施)來實作,如果沒有按照 interface 的規範,就會報錯
class Bird implements animals {
name: string;
category: string = "Birds"; //假設這行沒寫就會報錯
id: number = 9453;
constructor(name: string) {
this.name = name;
}
move(): void {
console.log(\`${this.name} 開始加速\`);
}
}

以上大概只是 TypeScript 的冰山一角,希望之後還有時間能更深入的了解 TypesScript。