TypeScript 3.8以降では、private
キーワードを使用してメンバーをプライベートとしてマークすることの違いは何ですか?
class PrivateKeywordClass {
private value = 1;
}
そしてJavaScriptのために提案された#
プライベートフィールドを使用します:
class PrivateFieldClass {
#value = 1;
}
どちらを優先するべきですか?
TypeScript 3.8以降では、private
キーワードを使用してメンバーをプライベートとしてマークすることの違いは何ですか?
class PrivateKeywordClass {
private value = 1;
}
そしてJavaScriptのために提案された#
プライベートフィールドを使用します:
class PrivateFieldClass {
#value = 1;
}
どちらを優先するべきですか?
回答:
TypeScript のプライベートキーワードは、コンパイル時の注釈です。これは、プロパティがそのクラス内でのみアクセス可能であることをコンパイラーに伝えます。
class PrivateKeywordClass {
private value = 1;
}
const obj = new PrivateKeywordClass();
obj.value // compiler error: Property 'value' is private and only accessible within class 'PrivateKeywordClass'.
ただし、たとえば型情報をキャストすることで、コンパイル時のチェックを簡単に回避できます。
const obj = new PrivateKeywordClass();
(obj as any).value // no compile error
private
キーワードはまた、実行時に強制されていません
TypeScriptをJavaScriptにコンパイルするとき、private
キーワードは単に削除されます。
class PrivateKeywordClass {
private value = 1;
}
になる:
class PrivateKeywordClass {
constructor() {
this.value = 1;
}
}
これから、private
キーワードがランタイム保護を提供しない理由がわかります。生成されたJavaScriptでは、これは通常のJavaScriptプロパティにすぎません。
プライベートフィールドにより、実行時にプロパティがプライベートに保たれます。
class PrivateFieldClass {
#value = 1;
getValue() { return this.#value; }
}
const obj = new PrivateFieldClass();
// You can't access '#value' outside of class like this
obj.value === undefined // This is not the field you are looking for.
obj.getValue() === 1 // But the class itself can access the private field!
// Meanwhile, using a private field outside a class is a runtime syntax error:
obj.#value
// While trying to access the private fields of another class is
// a runtime type error:
class Other {
#value;
getValue(obj) {
return obj.#value // TypeError: Read of private field #value from an object which did not contain the field
}
}
new Other().getValue(new PrivateKeywordClass());
TypeScriptは、クラス外のプライベートフィールドを使用しようとすると、コンパイル時エラーも出力します。
プライベートフィールドはJavaScriptプロポーザルから取得され、通常のJavaScriptでも機能します。
TypeScriptでプライベートフィールドを使用し、es6
or などの古いバージョンのJavaScriptをターゲットにしている場合es2018
、TypeScriptはプライベートフィールドのランタイム動作をエミュレートするコードを生成しようとします
class PrivateFieldClass {
constructor() {
_x.set(this, 1);
}
}
_x = new WeakMap();
をターゲットesnext
にしている場合、TypeScriptはプライベートフィールドを発行します。
class PrivateFieldClass {
constructor() {
this.#x = 1;
}
#x;
}
それはあなたが達成しようとしていることに依存します。
private
キーワードは、罰金のデフォルトです。それは、それが達成するように設計されたものを達成し、TypeScript開発者によって長年首尾よく使用されてきました。また、既存のコードベースがある場合、プライベートフィールドを使用するようにすべてのコードを切り替える必要はありません。これはesnext
、TSがプライベートフィールドに対して発行するJSがパフォーマンスに影響を与える可能性があるため、ターゲティングしていない場合に特に当てはまります。また、プライベートフィールドには、private
キーワードとは微妙ですが重要な違いがあります。
ただし、ランタイムプライベートを適用する必要がある場合やesnext
JavaScript を出力する場合は、プライベートフィールドを使用する必要があります。
また、JavaScript / TypeScriptエコシステム内でプライベートフィールドが広く普及するにつれて、どちらか一方を使用する際の組織/コミュニティの規約も進化することにも注意してください。
Object.getOwnPropertyNames
と同様のメソッドによってプライベートフィールドが返されない
プライベートフィールドはシリアル化されません JSON.stringify
継承に関する重要なエッジケースがあります。
たとえばTypeScriptは、スーパークラスのプライベートプロパティと同じ名前のサブクラスのプライベートプロパティを宣言することを禁止します。
class Base {
private value = 1;
}
class Sub extends Base {
private value = 2; // Compile error:
}
これはプライベートフィールドには当てはまりません。
class Base {
#value = 1;
}
class Sub extends Base {
#value = 2; // Not an error
}
private
初期化子のないキーワード私有財産は、放出されたJavaScriptでプロパティ宣言を生成しません。
class PrivateKeywordClass {
private value?: string;
getValue() { return this.value; }
}
コンパイルして:
class PrivateKeywordClass {
getValue() { return this.value; }
}
一方、プライベートフィールドは常にプロパティ宣言を生成します。
class PrivateKeywordClass {
#value?: string;
getValue() { return this.#value; }
}
コンパイルする(ターゲット設定時esnext
):
class PrivateKeywordClass {
#value;
getValue() { return this.#value; }
}
#
-privateフィールド序文:
#
-private、hard private、runtime-private#
-privateフィールドは、コンパイル時および実行時のプライバシーを提供しますが、これは「ハッキング可能」ではありません。これは、クラス本体の外部からメンバーに直接アクセスできないようにするメカニズムです。
class A {
#a: number;
constructor(a: number) {
this.#a = a;
}
}
let foo: A = new A(42);
foo.#a; // error, not allowed outside class bodies
(foo as any).#bar; // still nope.
#
-privateフィールドは一意のスコープを取得します。クラス階層は、同じ名前のプライベートプロパティを誤って上書きすることなく実装できます。
class A {
#a = "a";
fnA() { return this.#a; }
}
class B extends A {
#a = "b";
fnB() { return this.#a; }
}
const b = new B();
b.fnA(); // returns "a" ; unique property #a in A is still retained
b.fnB(); // returns "b"
private
プロパティが上書きされる危険がある場合、TSコンパイラは幸いにもエラーを発行します(この例を参照)。しかし、コンパイル時機能の性質上、コンパイルエラーが無視されたり、放出されたJSコードが利用されたりすると、実行時にすべてが可能になります。
ライブラリの作成者は#
、クライアントに重大な変更を加えることなく、プライベート識別子をリファクタリングできます。反対側のライブラリユーザーは、内部フィールドへのアクセスから保護されています。
#
-privateフィールドを省略します組み込みのJS関数およびメソッドは、#
-privateフィールドを無視します。これにより、実行時にプロパティの選択がより予測可能になります。例:Object.keys
、Object.entries
、JSON.stringify
、for..in
ループなど(コードサンプル、またマットBiernerの参照答えを):
class Foo {
#bar = 42;
baz = "huhu";
}
Object.keys(new Foo()); // [ "baz" ]
private
キーワード序文:
private
TSドキュメントのキーワード private
クラスのメンバーは、実行時の従来のプロパティです。この柔軟性を利用して、クラスの内部APIまたは状態に外部からアクセスできます。コンパイラチェック、タイプアサーション、動的プロパティアクセスなどのメカニズムを満足させるために、@ts-ignore
とりわけ使用できます。
型アサーション(as
/ <>
)とany
型付き変数の割り当ての例:
class A {
constructor(private a: number) { }
}
const a = new A(10);
a.a; // TS compile error
(a as any).a; // works
const casted: any = a; casted.a // works
TS private
は、escape-hatchを使用してメンバーの動的プロパティアクセスも許可します。
class C {
private foo = 10;
}
const res = new C()["foo"]; // 10, res has type number
プライベートアクセスはどこで意味がありますか?(1)単体テスト、(2)デバッグ/ログ記録の状況、または(3)プロジェクト内部クラス(オープンエンドリスト)を使用したその他の高度なケースシナリオ。
内部変数へのアクセスは少し矛盾しています-そうでなければprivate
、最初にそれらを作成しなかっただろう 。例をあげると、単体テストは、実装の詳細として非表示のプライベートフィールドを持つブラック/グレーのボックスであると想定されています。ただし、実際には、ケースごとに有効なアプローチがある場合があります。
TS private
修飾子は、すべてのESターゲットで使用できます。#
-privateフィールドはtarget
ES2015
/ ES6
以上でのみ使用できます。ES6 +では、WeakMap
内部的に下位レベルの実装として使用されます(こちらを参照)。#
現在、ネイティブプライベートフィールドにはが必要ですtarget
esnext
。
チームは、コーディングガイドラインとリンタールールを使用しprivate
て、を唯一のアクセス修飾子として使用することを強制できます。この制限は、一貫性を#
維持し、下位互換性のある方法で-privateフィールド表記との混乱を回避するのに役立ちます。
必要に応じて、パラメータープロパティ(コンストラクターの割り当ての省略形)がショーストッパーになります。これらはprivate
キーワードでのみ使用でき、-privateフィールドに実装する計画はまだありません#
。
private
一部のダウンレベリングのケースでは、実行時のパフォーマンスが向上する可能性があります(ここを参照)。private
キーワード表記をよりよく好みます😊。どちらのアプローチでも、コンパイル時にある種の名義型またはブランド型が作成されます。
class A1 { private a = 0; }
class A2 { private a = 42; }
const a: A1 = new A2();
// error: "separate declarations of a private property 'a'"
// same with hard private fields
また、どちらもインスタンス間のアクセスを許可します。クラスA
のA
インスタンスは他のインスタンスのプライベートメンバーにアクセスできます。
class A {
private a = 0;
method(arg: A) {
console.log(arg.a); // works
}
}
出典