---
# System prepended metadata

title: ' TypeScript Class ( 類別 )'
tags: [TypeScript]

---

# TypeScript Class ( 類別 )

![image](https://hackmd.io/_uploads/H1N77a2fC.png)

類（Class）是一種支持**物件導向**程式設計的核心結構。
它允許開發者創建具有特定屬性和方法的**藍圖**或**模板**。
**可被繼承**
```
class Person {
    name: string;
    age: number;
    gender: string;
    height: number;
    weight: number;
}
// 使用new 把 Person 的類別實體出來
const leo = new Person();

leo.name = "Leo 李歐";
leo.age = 18;
leo.gender = '鋼鐵直男';
leo.height = 179;
leo.weight = 50;
```
透過直接賦予上去，跟類別 Person根本上還是不一樣的東西
```
const leo: Person = {
    name:"Leo 李歐",
    age:18,
    gender:'鋼鐵直男',
    height:179,
    weight:50,

}
console.log(leo instanceof Person); // false
```

## 常用寫法
_ => 慣用寫法，通常代表私有的。例如 _ name 
不加_的 => 別人就可以直接使用，例如: leo.name 來取資料
### 正常寫法
```
class Person {
    // 創建和初始化在類或子類中創建的對象
    constructor(
        private _name: string ,
        private _age: number ,
        private _gender: string ,
        private _height: number ,
        private _weight: number ,
    ){}
    // get 取得。可以宣告 回傳的函式型別，不可帶入參數
    get name():number {
        return this._name;
    }
    // set 設置。不可宣告 回傳的函式型別 會報錯 
    set name(name:string){
        this._name=name;
    }
    // ...
    
    get bmi(){
    return this._weight / Math.pow(this._height / 100,2);
    }
    
    get profile(){
        return `${this._name}(${this._age}歲)`
    }
    // ...
}
const leo = new Person('Leo 里歐',18,'男',179,50)

consoel.log(leo.profile); // Leo 里歐 (18歲)
consoel.log(leo.bmi()); // 25.7 之類
```
### 一般寫法
```
class Person {
    name: string = '';
    age: number = 0;
    gender: string = '';
    height: number = 0;
    weight: number = 0;
    
    // 創建和初始化在類或子類中創建的對象
    constructor(
        _name: string ,
        _age: number ,
        _gender: string ,
        _height: number ,
        _weight: number ,
    ){
        this.name = _name;
        this.age = _age;
        this.gender = _gender;
        this.height = _height;
        this.weight = _weight;
    }
}
const leo = new Person('Leo 里歐',18,'男',179,50)
```
#### 縮寫法 =>
```
class Person {
    constructor(
        public name: string,
        public age: number,
        public gender: string,
        public height: number,
        public weight: number,
    ){}
}
const leo = new Person('Leo 里歐',18,'男',179,50)
```

## 繼承extends && super
```
class Person {
    constructor(
        protected name: string,
        protected age: number,
        protected gender: string,
        protected height: number,
        protected weight: number,
    ){}
}
class Taiwanese extends Person {
    private readonly _from:string = 'Taiwan' // readOnly 只能在建構子中初始化
    
    constructor(
         name: string,
         age: number,
         gender: string,
         height: number,
         weight: number,
    ){
        super(_name,_age,_gender,_height,_weight);
    }
    
    greet():void {
        console.log(`Hi , I'm ${this._name}. I come from ${this._from}`)
    }
}

const leo = new Taiwanese('Leo 里歐',18,'男',179,50);
leo.greet(); // Hi , I'm  里歐. I come from Taiwan
```
### super 在構造函數中的使用
當子類繼承自父類時，子類的構造函數必須調用 super()，這樣才能正確初始化父類的部分。這通常是在子類構造函數的第一行進行。
```
class Parent {
  constructor(protected name: string) {
    console.log(`Parent constructor: ${this.name}`);
  }
}

class Child extends Parent {
  constructor(name: string, private age: number) {
    super(name);  // 調用父類的構造函數
    console.log(`Child constructor: ${this.name}, Age: ${this.age}`);
  }
}

let child = new Child("John", 30);

```
在這個例子中，Child 類繼承了 Parent 類。子類的構造函數中使用 super(name) 調用了父類的構造函數，確保 name 被正確初始化

### super 在方法中的使用
super 也可以用來調用父類的方法，這尤其在覆蓋（Override）方法時很有用，當你想要擴展或修改父類方法的行為而不是完全替換它時。

這兩個例子展示了如何使用 super 在不完全重寫父類方法的情況下，擴展或修改其行為。這種方式允許子類建立在父類現有功能的基礎上，從而增加新的功能或修改行為，而不破壞或複製父類的原有實現。
#### 例子1
```
class Parent {
  sayHello() {
    console.log("Hello from Parent");
  }
}

class Child extends Parent {
  sayHello() {
    super.sayHello();  // 調用父類的 sayHello 方法，插進去來
    console.log("Hello from Child");
  }
}

let child = new Child();
child.sayHello();

```
這裡，Child 類的 sayHello 方法首先調用 Parent 類的 sayHello 方法，然後添加它自己的行為。這種方式允許子類構建在父類功能之上，而不是從頭開始。

#### 例子2
假設有一個 DataProcessor 類，它具有一個處理數據的基本方法。你想創建一個子類來在處理前驗證數據，並在處理後檢查結果。
```
class DataProcessor {
  processData(data: string) {
    console.log(`Processing data: ${data}`);
    // 假設處理數據的邏輯
    return `Processed: ${data}`;
  }
}

class ValidatingDataProcessor extends DataProcessor {
  processData(data: string) {
    if (!data) {
      throw new Error("No data provided!");
    }
    console.log("Data validation passed.");
    const result = super.processData(data);  // 調用父類的 processData 方法
    console.log(`Data processed with result: ${result}`);
    return result;
  }
}

const processor = new ValidatingDataProcessor();
processor.processData("Sample data.");
```
在這個例子中，ValidatingDataProcessor 覆蓋了 DataProcessor 的 processData 方法。在其自己的方法中，它首先檢查數據是否為空，然後記錄一條數據驗證通過的消息，接著調用父類的 processData 方法處理數據，最後輸出處理結果。

### super 訪問父類屬性
雖然在 TypeScript 中，super 主要用於調用父類的方法和構造函數，但它也可以用來訪問父類中的屬性，尤其是當這些屬性被子類的方法或構造函數訪問時。
```
class Parent {
  protected value: number = 42;
}

class Child extends Parent {
  displayValue() {
    console.log(`Value: ${super.value}`);  // 訪問父類的 protected 屬性
  }
}

let child = new Child();
child.displayValue();

```
在這個例子中，Child 類通過 super.value 訪問了 Parent 類的 value 屬性。


## 運算子
###  constructor （建構函數）
constructor 是一個特殊的方法，用於創建和初始化在類或子類中創建的對象。每個類只能有一個 constructor。如果未顯式定義，一個空的 constructor 將被默認添加。
```
class Person {
  constructor(
  public name: string,
  public age: number
  ) {
    // 初始化物件時，自動設定 name 和 age 屬性
  }
}
```
在這個例子中，constructor 接受兩個參數並將它們設為公共屬性。

###  public（公開修飾符）
1. public - 任何人都可以透過該類別的實體來存取該屬性的值或使用該函
式。（不宣告時預設為 public ）
2. protected - 只有在該類別或其子類別裡才能存取該屬性的值或是使用該
函式。
3. private - 只有在該類別裡才能存取該屬性的值或使用該函式

在 TypeScript 中，public 是一個存取修飾符，用於設定類成員（屬性或方法）的可見性。public 成員可以在任何地方被自由訪問，是預設的存取級別。
```
class Animal {
  public species: string;

  constructor(species: string) {
    this.species = species;
  }

  public display(): void {
    console.log(`This animal is a ${this.species}.`);
  }
}

let cat = new Animal("Cat");
cat.display();  // 輸出: This animal is a Cat.

```
這個 Animal 類定義了一個公開方法 display 和一個公開屬性 species。

### protected（受保護的修飾符）
protected 修飾符類似於 private，但它允許在子類中訪問父類中的受保護成員。
```
class Parent {
  protected protectedMethod(): void {
    console.log('Protected method');
  }
}

class Child extends Parent {
  public test(): void {
    this.protectedMethod();  // 可以訪問受保護方法
  }
}

let child = new Child();
child.test();  // 正常運作
// child.protectedMethod(); // 錯誤: 'protectedMethod' 是受保護的。
```

### private（私有修飾符）封裝概念
private 修飾符用於限制類成員（屬性或方法）的訪問範圍。當一個成員被標記為 private 時，它只能在定義它的類內部被訪問。
```
class Example {
  private secretMethod(): void {
    console.log('This is private.');
  }

  public show(): void {
    this.secretMethod();
  }
}

let example = new Example();
example.show();  // 可以間接訪問 private 方法
// example.secretMethod(); // 錯誤: 'secretMethod' 是私有的，只能在類 'Example' 內部訪問。

```


### instanceof （實例運算子）
instanceof 運算子用於測試一個物件是否為某個類的實例，或者說這個物件是否使用這個類或其子類之一構造。
```
class Car {
  constructor(
  public brand: string
  ) {}
}

let myCar = new Car("Toyota");

console.log(myCar instanceof Car);  // 輸出: true
console.log(myCar instanceof Animal);  // 輸出: false

```
在這個例子中，myCar 是 Car 類的實例，所以 myCar instanceof Car 返回 true，而 myCar instanceof Animal 則返回 false，因為它不是 Animal 類的實例。

### static（靜態修飾符）
static 修飾符用於定義類的靜態成員，這些成員可以不實例化類就可以訪問。靜態成員屬於類本身，而不是任何類的實例。
```
class StaticExample {
  static staticProperty = "Static property";

  static staticMethod(): void {
    console.log('This is a static method.');
  }
}

console.log(StaticExample.staticProperty);  // 直接訪問靜態屬性
StaticExample.staticMethod();  // 直接呼叫靜態方法
```
### abstract（抽象類和方法）
抽象類是供其他類繼承的基類，不能直接被實例化。抽象類中可以包含抽象方法，這些方法必須在派生類中被實現。
```
abstract class AbstractClass {
  abstract abstractMethod(): void;
}

class ConcreteClass extends AbstractClass {
  abstractMethod(): void {
    console.log('Implemented abstract method');
  }
}

let concrete = new ConcreteClass();
concrete.abstractMethod();  // 實現了抽象方法
// let abstract = new AbstractClass(); // 錯誤: 不能創建抽象類的實例
```

###  Override(覆蓋)
「覆蓋」（Override）是一個核心概念，尤其在使用如 TypeScript 這樣的語言時更是如此。覆蓋發生於子類中，當子類重新定義繼承自父類的方法時，這個重新定義的方法將會替換原有的方法實現。

即使沒有使用 override 關鍵字，子類中的方法仍然可以覆蓋（override）父類中的方法。override 關鍵字不是必需的。

TypeScript 4.3 版本引入了一個新的 override 關鍵字，用於明確標記子類中應該覆蓋的方法。這有助於捕捉錯誤，比如當預期覆蓋父類的某個方法而父類中實際沒有該方法時，TypeScript 編譯器會報錯。

#### 為何使用 Override
* 增強或改變功能：子類可以擴展或修改繼承的方法功能，使其更適合子類的需要。
* 多態性：通過覆蓋，相同的方法名在不同的類中可以有不同的實現，這是多態性的一種表現，允許在運行時根據對象的實際類型調用相應的方法。

#### 示例

```
class BaseClass {
  greet() {
    console.log("Hello from BaseClass");
  }
}

class DerivedClass extends BaseClass {
  override greet() {
    console.log("Hello from DerivedClass");
  }
}

const base = new BaseClass();
base.greet();  // 輸出: Hello from BaseClass

const derived = new DerivedClass();
derived.greet();  // 輸出: Hello from DerivedClass
```

在這個例子中：

* BaseClass 定義了一個 greet 方法。
* DerivedClass 繼承自 BaseClass 並覆蓋了 greet 方法，使用了 override 關鍵字來明確表示這是一個覆蓋的行為。
* 當 DerivedClass 的實例調用 greet 方法時，它使用的是 DerivedClass 中定義的版本。

好處
使用 override 關鍵字的好處包括：

* 更清晰的意圖表達：代碼更易讀，因為其他開發者可以清楚地看到哪些方法是故意覆蓋的。
* 編譯時安全：如果父類中沒有要覆蓋的方法，或者父類方法被移除或改名，TypeScript 編譯器會立即報錯，這有助於提早捕捉到潛在的錯誤。