Programmingフロントエンド開発者

TypeScriptのアクセス修飾子(public、private、protected)はどのように機能しますか?継承を使用する際とJavaScriptへのトランスパイル時の注意点は何ですか?

Hintsage AIアシスタントで面接を突破

答え

TypeScriptでは、アクセス修飾子はクラス内のプロパティおよびメソッドの可視範囲を制限するために使用されます:

  • public — プロパティまたはメソッドはどこでもアクセス可能(デフォルト)。
  • private — 宣言されたクラス内でのみアクセス可能。
  • protected — クラスおよびその子クラス内でアクセス可能。
class Animal { public name: string; private age: number; protected kind: string; constructor(name: string, age: number, kind: string) { this.name = name; this.age = age; this.kind = kind; } } class Dog extends Animal { bark() { console.log(this.kind); // OK: protected // console.log(this.age); // エラー: privateは子クラスで見えない } } const dog = new Dog('シャリク', 5, '哺乳類'); console.log(dog.name); // OK // console.log(dog.kind); // エラー: protected // console.log(dog.age); // エラー: private

注意点:

  • コンパイルされたJavaScriptでは、privateおよびprotected修飾子はコンパイル時のチェックのみに実装され、ランタイムでの物理的な隔離はありません!JSコードはすべてのプロパティを含む(例えば、オブジェクトを巡回することでアクセス可能)。
  • 新しいバージョン(TypeScript 3.8以降)では、本物のプライベートフィールドの構文 #field が導入されましたが、これはすでにJSの仕様であり、異なる方法でコンパイルされます。

ひっかけ問題

質問: TypeScriptクラスのprivateプロパティに、JavaScriptにコンパイルされた後にアクセスできますか?

答え: はい、private(およびprotected)はコンパイル時のTypeScriptのチェックであり、ES5またはES6にコンパイルされた後はプライバシーが保持されず、プロパティはオブジェクトに残り、プロパティ名によるアクセスが可能です(例えば、object['privateProp']を通じて)。

// コンパイル後のJSコード function Animal(name, age, kind) { this.name = name; this.age = age; this.kind = kind; } var dog = new Animal('シャリク', 5, '哺乳類'); console.log(dog['age']); // 5 — TSレベルでのアクセス制限のみ!

このテーマの微妙な点を知らないために起こる実際のエラーの例。


物語

大規模なプロジェクトで、開発者はランタイムでのprivateフィールドのアクセス不可に頼っていました。その結果、オブジェクトをシリアライズ(JSON.stringify)してログに送信する際に、機密データが偶然に漏洩しました。なぜなら、TSの型チェックは実際のフィールドへのアクセスからは保護できなかったからです。


物語

プロジェクトでは、クラスのインスタンスを動的にプロパティを追加することで「拡張」するメカニズムが実装されました。動的に追加されたプロパティがprivate名を上書きし、そのために外部コードによって偶然に修正されてしまいました。エラーは生産環境でのみ発見され、プライバシーは確保されませんでした。


物語

JavaScriptからTypeScriptへの移行時に、チームはprotectedを使用することにしましたが、それが子クラス外からのフィールド利用を防ぐと思っていました。しかし、プログラマーはObject.assignを通じて間違ってprotectedフィールドをランタイムで上書きし、デバッグの難しいバグを引き起こしました。これは、ランタイムのカプセル化がなかったため可能になりました。