JavaScript TypeScript private field TS3.8

TypeScript 私有類別屬性 (Private Class Fields)

林承甫 Chengfu Lin 2020/09/01 15:27:33
2076

在 JavaScript,class 屬性預設都是 public

即使在 TypeScript, 使用者仍可透過 work-around 的方式,存取有 private 關鍵字修飾的屬性

如以下這段 TypeScript:

class Foo {
  private bar = "private";
}

let instance = new Foo();
console.log(instance["bar"]); // work-around
console.log(instance.bar); // Error in compile time
// error: Property 'bar' is private and only accessible within class 'Foo'.

而現在,JS 提供新的方式定義 private field

如類別屬性,名稱需前綴 #,且需先宣告後使用,並無法從 class 外部存取

如以下這段 JS:

class B {
    #hello; // private field

    constructor() {
        this.#hello = "hello";
        this.#none = "none"; // Syntax error
    }
    
    hello() {
    	console.log(this.#hello);
    }
}

let b = new B();
b.hello();
console.log(b.#hello === "hello"); // Syntax error

而 TypeScript 在 3.8 開始支援使用 JS private fields

需要注意以下幾點:

  • 如以上提及,屬性名稱需前綴 #
  • 每個 private field 存在範圍只在所擁有的 class 中 ("Every private field name is uniquely scoped to its containing class") (以下補充說明)
  • 只可在所屬的 class 中存取使用到,無法透過其他 JS 方式於 class 外部存取
  • 不可與 publicprivate 修飾關鍵字一併使用

在以下繼承關係的類別中,可以各自擁有相同名稱的 private field,並不會被 override

相對使用 private 修飾詞時,則不可重複定義

會出現 Types have separate declarations of a private property 的錯誤

class A {
  #foo = 2;
  bar = 3;

  showFooA() {
    console.log(this.#foo);
  }

  showBarA() {
    console.log(this.bar);
  }
}

class B extends A {
  #foo = 4;
  bar = 5;

  showFooB() {
    console.log(this.#foo);
  }

  showBarB() {
    console.log(this.bar);
  }
}

let b = new B();
// `this.#foo` refers to different property within each class
b.showFooA(); // 2
b.showFooB(); // 4
// `this.bar` refers to the same property
b.showBarA(); // 5
b.showBarB(); // 5

最後,

要在 TypeScript 使用 private fields 的話,需要指定 compiler target 為 ES2015 或以上

這與底層的實作方式有關,而使用 private 修飾詞定義的方式則可於所有 target 環境使用

References

林承甫 Chengfu Lin