問題の背景
TypeScriptでは、プライベートプロパティは最初にprivate修飾子を使用して導入され、ECMAScript 2021からJavaScriptでは#シンボルを使用した真正なプライベートフィールドがサポートされています。主な目的は、データをカプセル化し、クラスの内部詳細への外部アクセスを防ぐことです。
問題
TypeScriptのprivate修飾子はコンパイル時にのみプライバシーを保証しますが、コンパイルされたJavaScriptではこれらのフィールドはオブジェクトのプロパティに対する直接アクセスを通じて依然としてアクセス可能です。これにより、オブジェクトの状態が意図せず変更される可能性があります。ESプライベートフィールド(#)は、クラスの外からはアクセスできず、JSのランタイムレベルでもアクセス不能であり、データの真正な保護を提供します。
解決策
TypeScriptは両方のアプローチをサポートしています。どちらを選択するかは、必要な保護の程度とJavaScriptバージョンとの互換性の制限を考慮して決定してください。
コード例:
class Example { private hidden: number; #trulyHidden: string; constructor() { this.hidden = 42; this.#trulyHidden = 'secret'; } getHidden() { return this.hidden; } getTrulyHidden() { return this.#trulyHidden; } } const x = new Example(); // x.hidden — TSでエラーだが、JSでx['hidden']を介しては有効 // x.#trulyHidden — JSでも構文エラー
重要な特徴:
privateはTypeScriptコンパイル時にのみプライバシーを保証します#fieldはJavaScript実行時レベルでの真正なプライバシーを提供します#の使用は、現代のJS(ES2021以降)のサポートが必要ですTypeScriptのプライベートフィールドにJavaScriptから直接アクセスすることは可能ですか?
はい、可能です。プライバシーはコンパイル時にのみ実施されるため、元のJSではフィールドは通常のオブジェクトのプロパティとして存在します。例えば:
class A { private x = 1; } const a = new A(); console.log((a as any)["x"]); // 1
インターフェースでプライベートフィールドを#を使って使用したり、typeで記述したりできますか?
いいえ、#privateフィールドはクラスの実装の一部であり、インターフェースやタイプで記述されたり、クラスの外で使用したりすることはできません。インターフェースはパブリックメンバーのみを記述します。
#を使って宣言されたプライベートフィールドを継承できますか?
いいえ、そのようなフィールドはサブクラスで全くアクセスできません。アクセスできるのはそのクラス自身のみです:
class Parent { #foo = 123; } class Child extends Parent { // this.#foo = 444; // エラー }
privateを使用することは、直接アクセスによる脆弱性につながる#fieldsを使用しようとする試みパスワードを保存するプロジェクトのUserクラスにはprivate passwordフィールドが使用されています。開発者の1人がデバッグのためにuser['password']を通じてアクセスしてしまい、そのフィールドが外部モジュールによって変更されてしまいました。
利点:
欠点:
開発者がESプライベートフィールド#passwordを使用しています。これにより、インデックスを介したり継承先でのフィールドへのアクセス試みは失敗し、外部ライブラリやツールを使用してもデータ保護が確保されています。
利点:
欠点: