TypeScript(6) 物件繼承

TypeScript(6) 物件繼承

了解物件可以有私有/保護屬性之後,接下來我們要來看物件的繼承,繼承可以讓一個全新的類別被實體化(new 出來)的時候,就能夠直接取用某個類別的方法。通常我們稱被繼承的類別為「父類別」,繼承別人的物件稱為「子類別」。

題外話,早期 Javascript 必須透過 property 來達到繼承,但 ES6 之後也有類別(class)繼承的做法。

假設我今天想要寫一隻程式來記錄動物的行為,於是我創造了一個 Animal 類別,裡面有一個 move 方法,可以描述某個動物移動了幾步。

class Animal {   
    name:string;
    constructor(theName: string) { this.name = theName; }
     move(meters: number) {
        console.log((this.name + " moved " + meters + "m."))
    }
  }

但動物有很多種,我想細分動物的種類,也一樣共用描述行為的這些方法,該怎麼做到?這時候就需要用到繼承,Typescript 裡面使用 extends 語法來進行繼承。

class Snake extends Animal {  

    constructor(name: string) { 
        super(name);  
    }

    move() {  
        super.move(5);  
    }
}

並且使用 super 來取得父類別的屬性以及方法(也就是 Animal ),且記得如果在子類別有使用建構子,一開始要先使用spuer()呼叫父類別建構子,否則會報錯。

在Snake裡面如果寫入跟父類別 Animal 相同的方法,我們就稱這個動作為「複寫」(覆蓋掉父類別方法),但是以上述程式碼為例,就算不複寫,snake 一樣可以直接調用 move 方法(因為繼承。

TypeScript(5) 物件屬性的修飾子

Typescript 跟一般 OOP 的語言寫法相似,所以講到物件一定少不了一些物件的基本觀念,修飾子是用來規定物件屬性(或稱成員) 存取權限的關鍵字,大致有以下三種:

  • public 公有屬性,可以被自由存取的屬性
  • private 私有屬性,只限於該屬性所屬的類別內可以存取
  • protected 受保護屬性,除了該屬性所屬類別,該類別之子類別(繼承該類別的類別)內也能存取。

修飾子使用方式

Typescript 裡面,沒有加上修飾子的屬性,預設都會被認為是public,如果想要加上其他修飾子的話,只要在變數宣告前面加上修飾子就可以了:

class Person {
    private name: string;
    constructor(name: string) { this.name = name; }
}

Getter / Setter

我們可以透過 getter 以及 setter 來當作統一出入口,來對無法直接存取的私有變數進行操作, getter 及 setter 跟 JS ES6 的使用方式相同。

 class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }
}


class Employee extends Person {
    
    constructor(name:string){
        super(name) //把 name 傳給父類別的建構子
    } 
    get nameGetter():string{
        return this.name
    }
    set nameSetter(value){
        this.name = value 
    }
    
}

let Jack = new Employee('Jack') 
console.log(Jack)

TypeScript(4) 進階型別 never、void、null與undefined

除了基本型別外,還有一些型別是比較不常見的,必須要先了解函式型別跟Union Type,所以放在這邊講,以下將個別介紹:

  • Void
  • Never
  • Null
  • Undefined

Void

void用於function的回傳值定義,代表這個function不會回傳任何值:

function returnNothing() : void{
    console.log('returnNothing')
}

Never

never這個型別跟void很像,但定義上更嚴謹,有些function根本不會結束,例如拋出例外,或是無窮迴圈,never就是用來代表這些函式行為的類型:

 function errorMsg(message:string): never{
    throw new Error(message);
 }

Null 與 Undefined

就算再JS裡面,null跟undefined依然是很特殊的型別。TS裡面,可以選擇要不要嚴格檢查,在tsconfig.json裡面可以設定strictNullChecks(預設為true)。

如為true,代表嚴格null檢查,變數只要設定為其他型別就不能為null(undefined)反過來講如為false,則代表所有型別變數都可以設為null(undefined),無需另外撰寫規則。

tsconfig.json :


"compilerOptions": {
    ...
    "strictNullChecks":false
    ...
}
let canBeNull:number = 12; 
canBeNull = null;//it's ok with  strictNullChecks:false

在嚴格null檢查的模式下,如要達成上面的行為,我們就必須用到Union Types:

let canBeNull:number |null  = 12; 
canBeNull = null //it's still ok even with strictNullChecks:true

TypeScript(3) Union Types與Custom Type

TypeScript(3) Union Types與Custom Type

Union Types

當某個變數需要可以同時容納兩種以上的型別時,我們可以在宣告型別的時候使用常見用於or邏輯的「|」來宣告型別:

let myAge : number | string = 27; 

上面的例子代表這個變數可以同時容納兩種以上的型別。

Custom Type

結合前幾章節講到的所有知識,當你的物件結構很複雜,又想要針對每個property宣告型別時,就可以使用「type」關鍵字來宣告自訂的型別:

type ComplexType = {
    data: number[] , 
    output:(all:boolean) => number[] 
};

let complexData: ComplexType = {
    data:[100,3.99,10], 
    output:function (all: boolean): number[]{
        return this.data 
    } 
}

TypeScript(2) - 函式宣告、函式型別與物件

基本宣告方式

函式本身的型別宣告可能在幾個地方發生,分別是參數跟回傳值:

function multiply(value1: number, value2: number): number{
    return value1*value2
} 

console.log(multiply(2,3)); //6

當然也可以像JS原本的宣告方式一樣,如此一來預設的類型會是Any,不過這樣使用TS就失去意義:

let myName:string = 'Moojing'
function returnMuyName (){
    return myName;
} 

函式型別

JS 裡面,我們可以把 function assign 給變數,但是 TypeScript 裡面想要啟用型別檢查,就一樣必須在變數後面聲明這個函式的型別,(包含參數和回傳值),但如果一個變數沒有任何型別的話,這個 function 的型別跟一般變數一樣會被設為 any ,所以可以傳入型別。

function sayHello():void{
    console.log('Hello')
} 

function multiply(value1:number,value2:number): number{
    return value1*value2
} 

let doSomeThing = sayHello;
let doSomeThing = multiply;

但是這樣一來我們就無法直觀的判斷這個變數的型別,畢竟使用TS就是要將他的優點發揮到極致,還記得之前我們提過變數型別的宣告嗎?我們可以這麼做:

let doSomeThing: (val1: number, val2: number)=>number;
    doSomeThing = multiply;
    doSomeThing = sayHello;// Type '() => void' is not assignable to type '(val1: number, val2: number) => number'.

這樣一來在assign錯誤的函式型別時,TS就能夠檢查出錯誤,不過這樣一來函式的宣告就變得有點的雜亂不易閱讀,這時我們可以使用 type 關鍵字把函式型別抽出來另外宣告

    
    type someType = (val1: number, val2: number)=>number; 

物件型別

將物件assign給某變數,該變數的型別宣告方式,其實跟前面function型別的宣告方法差不多:

let userData : {name: string, age: number}= {
    name: "Max", 
    age:27
}

宣告完物件型別之後,就不能再assign該物件型別裡沒有定義的property跟類型:

userData = {
    a:'hello', //類型 '{ a: string; b: number; }' 不可指派給類型 '{ name: string; age: number; }'。
    b:22
}

TypeScript(1) - 基本型別介紹

變數宣告方式

  1. 指定型別
    指定型別的宣告其實跟一般宣告方式差不多,只是在變數後面多了一個「:<要宣告的型別>」的指定,指定之後赴值一樣只能是同行別的值:
let name: string = 'moojing';
  1. 不指定型別
    Typescript裡宣告變數,可以不指定型別,那就會是類似Js的方法,:
let name = 'moojing';

型別不指定的話,TS會自動偵測赴與的值是什麼類型,但是如果再次赴與不同型別的值,編譯就會報錯:

name = 2 ; //Error: Type '2' is not assignable to type 'string'.

TypeScript的幾種型別

TypeScript 裡面的型別基本上跟JS原生的型別相同,只是因為型別系統的加入又多出了幾種型別,:

  • Any
  • String
  • Number
  • Boolean
  • Array
  • Object
  • Tuples
  • Enums
  • Void
  • Never

Any

Any型別顧名思義就是任何型別都可以放入這個變數,如果使用這個型別,基本上就跟原本Js可以隨意assign的使用方式ㄧ樣了。實務上不太建議使用Any,因為這樣就跟使用JS沒有兩樣了:

let anyType:any = 1234;
anyType='1234' //It's Okay.

String / Number / Boolean

這三種基本型別的宣告都很直觀,直接宣告即可:

 let myName:string = "Moojing"
 let myName:number = 123
 let myName:boolean = true

Array

Array的型別有兩種宣告方式:

 let array:Array<number> = [2,3,4]
  let array1:number[]=[4,5,6]

也可以宣告型別為any的Array:

 let array1:any[]=[4,5,6]

Tuples 元組

Tuples在數學上的意思是有限的序列組合,其實就是包含很多不同型別元素的陣列:

let address: [string,number] = ["Superstreet",31]

不過使用Tuples就要注意,元素類型的順序也要跟宣告的相同,以上面的例子來說,第一個元素就不能是數字,第二個元素也不能是字串。

Enum 列舉

Enum就是把所有可能的狀態列出來:

enum Color{
    Red,
    Green,
    Black
} 

Enum的宣告方式很像一般物件,但是元素的值預設是向陣列的index,0,1,2…


enum Color{
   Red,
   Green,
   Black
} 

let myColor:Color = Color.Green; 
console.log('myColor: ', myColor); //1

不過你也可以自己決定每個狀態對應的值:

enum Color{
   Red =100,
   Green=200,
   Black=300
} 
let myColor:Color = Color.Green; 
console.log('myColor: ', myColor); //200
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×