[學習筆記]TypeScript查缺補漏(一):類

2023-11-01 06:01:37

@

基礎知識

建立型別

class Abc { }

類的初始化

const abc = new Abc();

型別和值

類既可以作為型別使用,也可以作為值使用。


const a:Bag = new Bag()

JSDoc 註釋

JSDoc 是 JavaScript 的一種註釋規範,它使用特定的註釋格式來自動生成 API 檔案。JSDoc 通過註釋來提取資訊,例如函數名、引數型別和返回型別等,然後使用這些資訊生成人類可讀的檔案。

範例:

/**  
 * 這是一個函數註釋  
 * @param {string} 引數名 - 引數描述  
 * @returns {number} 返回值描述  
 */  
function myFunction(引數名) {  
  // 函數實現  
  return 0;  
}

在這個例子中,/** 開始一個多行註釋,然後在註釋中使用 @param 和 @returns 來描述函數的引數和返回值。JSDoc 還支援其他註釋標籤,例如 @description、@type 和 @example 等。

欄位

class User extends Account implements Updatable, Serializable {
    id: string;                     //普通欄位
    displayName?: boolean;          //可選欄位
    name!: string;                  //非可選欄位
    #attributes: Map<any, any>;    //私有欄位
    roles = ["user"];               //有預設值的欄位
    readonly createdAt = new Date() // 帶有預設值的唯讀欄位
}

私有欄位

class Foo {  
    private myAny = 1;  
}  
  
class Bar {  
    #myAny = 1;  
}

私有成員只能在它們所屬的類內部存取,類的外部無法直接存取這些私有成員。

範例:

class MyClass {  
    #myPrivateVariable: string;  
  
    public myPublicMethod() {  
        console.log(this.#myPrivateVariable); // 正確,可以在類內部存取私有成員  
    }  
}  
  
const obj = new MyClass();  
console.log(obj.#myPrivateVariable); // 錯誤,私有成員無法從外部存取

區別
private在編譯後JavaScript中沒有影響,僅對TypeScript編譯器有影響,而使用#符號宣告的私有屬性在JavaScript中會被編譯為常規的私有屬性。

可選和非可選欄位

感嘆號(!)用於標記屬性或方法為非可選(non-optional)。這意味著該屬性或方法在類範例化時必須提供值,否則將導致編譯錯誤。

class Person {  
  constructor(public name: string, public age: number!) {  
  }  
}  

const person = new Person("Alice", 25); // 正確,age 屬性必須提供值  
const personOptional = new Person("Bob"); // 錯誤,age 屬性未提供值

問號(?)用於標記屬性或方法為可選(optional)。這意味著該屬性或方法在類範例化時可以省略,不會導致編譯錯誤。

class Person {  
  constructor(public name: string, public age?: number) {  
  }  
}  
  
const person = new Person("Alice"); // 正確,age 屬性未提供值  
const personOptional = new Person("Bob", 25); // 正確,age 屬性提供了值

欄位型別約束

[key: string]: number; 是一種物件型別的寫法,表示物件的鍵是字串型別,值是數位型別。

範例:

const person: { [key: string]: number } = {  
  age: 25,  
  height: 170,  
  weight: 65  
};

Getter/Setter

Getter 是一個獲取屬性的方法,Setter 是一個設定屬性的方法。可以使用 get 和 set 關鍵字來定義它們。
Getter/Setter可以在不改變屬性的存取許可權的情況下,對屬性的值進行更精細的控制。比如可以在讀取或設定屬性的值時新增額外的邏輯。

class Person {  
  private _name: string;  
  
  get name(): string {  
    return this._name;  
  }  
  
  set name(value: string) {  
    this._name = value;  
  }  
}  
  
let person = new Person();  
person.name = "John"; // 使用 setter 設定值  
console.log(person.name); // 使用 getter 獲取值,輸出 "John"

靜態成員

靜態方法中this指向類本身,而不是類的範例物件。

class StaticClass {  
    n?:number=4;

    //靜態欄位
    static s:number

    //靜態方法
    static staticMethod() {  
        this.s=5
        console.log('This is a static method');  
  }  
}

StaticClass.staticMethod(); // 呼叫靜態方法
var staticClass=new StaticClass();
console.log(staticClass.n)     //類成員不受影響 ,輸出4
console.log(staticClass.s)     //undefined 


console.log(StaticClass.n)     //undefined
console.log(StaticClass.s)     //靜態類成員不受影響 ,輸出5

函數過載

在 TypeScript 中,可以使用函數過載(Function Overloading)來定義多個同名函數,它們具有不同的引數型別或引數數量。這可以幫助提高程式碼的可讀性和可用性。

要實現函數過載,需要遵循以下規則:

  1. 過載的函數必須同名。
  2. 過載的函數引數型別或數量必須不同。
  3. 過載的函數可以有一個或多個過載。
  4. 函數過載不能改變函數的返回型別。

範例:

  update: (retryTimes: number) => void;
  update(retryTimes: number): void;

建構函式

建構函式是用於建立和初始化物件範例時候被呼叫的特殊方法,用於初始化物件的屬性併為其分配記憶體空間。

範例:

class Person {  
  private name: string;  
  private age: number;  
  
  constructor(name: string, age: number) {  
    this.name = name;  
    this.age = age;  
  }  
  
  greet() {  
    console.log(`名字 ${this.name} 年齡 ${this.age}`);  
  }  
}  
  
var person = new Person("John", 30);  
person.greet(); // 輸出 "名字 John 年齡 30" 

引數屬性

可以使用引數屬性(Parameter Properties)來在類中定義與函數引數相關的屬性。引數屬性提供了一種簡潔的方式來宣告與函數引數相關的屬性,而不需要顯式地使用 this 關鍵字。

範例:

class Person {  
  constructor(public name: string, public age: number) {}  
}

var person = new Person("John", 30);  
console.log(person.name); // 輸出 "John"  
console.log(person.age); // 輸出 30

類的範例化

  
  (): JSONResponse              //  可以通過 () 呼叫這個物件 -(JS中的函數是可以呼叫的物件) 
  new(s: string): JSONResponse; // 可以在此類物件上使用 new

範例:範例化泛型物件

class Person {  
  age= 25;
  height= 170;  
  weight= 65;
  constructor() {  
  }  
}  

class PersonService<TService> {
    Service?: TService;
    Init(service?: { new(): TService }) {
        if (service != null) {
            this.Service = new service();
        }
    }
}

var p = new PersonService<Person>(); 
p.Init(Person);
console.log(p.Service?.age);  // 25
console.log(p.Service?.height);  // 170
console.log(p.Service?.weight);  // 65

箭頭函數

在箭頭函數中,this不指向呼叫該函數的物件,而是指向定義該箭頭函數時的上下文。
儘管箭頭函數是在物件的方法中定義的,但是它不會捕獲到呼叫該方法的物件作為自己的this上下文。

範例:

let obj = {  
    value: "I am an object",  
    printValue: () => { console.log(this.value); }  
}  
  
obj.printValue(); // 輸出:"I am an object"

this的作用域

全域性

在全域性作用域或單獨的函數作用域中,this參照的是全域性物件。

console.log(this); // 在全域性作用域中輸出:window物件  
  
function testFunc() {  
    console.log(this); // 在函數作用域中輸出:window物件  
}  
  
testFunc();

類和物件方法

當函數作為物件的方法被呼叫時,this指的是obj物件。

let obj = {  
    name: 'Example Object',  
    printName: function() {  
        console.log(this.name);   
    }  
}  
  
obj.printName(); // 輸出:"Example Object"

當呼叫類中的函數時,this指的是類的範例物件。

class MyClass {  
    myMethod() {  
        console.log(this); // 輸出:MyClass的範例物件  
    }  
}  
  
const obj = new MyClass();  
obj.myMethod();

泛型

泛型是一種允許你在定義類、介面、函數和方法時使用型別引數的功能。泛型允許你編寫靈活的程式碼,同時保持型別安全。通過使用泛型,你可以在強型別環境中編寫可重用的程式碼,而無需擔心具體的型別實現細節。

泛型類


class Box<Type>{
    contents?: Type
    constructor(value: Type) {
    this.contents = value;
}}

var stringBox = new Box("a package");
console.log(stringBox.contents) // a package

泛型介面

interface Generator<T> {  
  generate(): T;  
}  
  
class RandomNumberGenerator implements Generator<number> {  
  generate() {  
    return Math.random();  
  }  
}  
  
let generator = new RandomNumberGenerator();  
let randomNumber = generator.generate(); // 型別為 number

泛型函數

function identity<T>(arg: T): T {  
  return arg;  
}  
  
let x = identity<number>(123); // 型別為 number  
let y = identity<string>("hello"); // 型別為 string

裝飾器

裝飾器是使用 @ 符號來標識的特殊型別的函數,可以用來擴充套件類或方法的行為。實現類似面向切面程式設計的特性。

可以在類、類方法、存取器、屬性和方法引數上使用裝飾器

範例:

function log(target: any, obj:any) {  
  console.log(target)
  console.log(`Creating instance of ${target.name}`);  
}  
  
@log  
class MyClass {  
  myMethod() {  
    console.log("Hello, World!");  
  }  
}  

const instance = new MyClass();

TypeScript範例可在https://www.typescriptlang.org/play中偵錯