TypeScriptにおける「private」キーワードとプライベートクラスフィールドの徹底比較
TypeScriptにおける「private」キーワードとプライベートクラスフィールドの違い
従来、TypeScriptではprivateキーワードを使ってプライベートなメンバを定義していました。しかし、TypeScript 3.8以降では、プライベートクラスフィールドと呼ばれる新しい機能が導入されました。
privateキーワードとプライベートクラスフィールドは、どちらもクラス内部からのみアクセスできるという点では同じですが、いくつかの重要な違いがあります。
構文
プライベートクラスフィールド
class Person { #name: string; // # 記号で始まる変名がプライベートクラスフィールド constructor(name: string) { this.#name = name; } getName(): string { return this.#name; } }
アクセス方法
- プライベートクラスフィールド
- クラス内部から、# 記号を使って直接アクセス可能
- 例:
this.#name
- privateキーワード
- クラス内部から、this. を使ってアクセス可能
デメリット
- プライベートクラスフィールド
- privateキーワード
- コンパイル時にチェックされないので、意図せぬアクセスが可能 (デバッグが困難)
- クラス継承時に、サブクラスから継承されてしまう可能性がある
項目 | privateキーワード | プライベートクラスフィールド |
---|---|---|
構文 | private name: | #name: |
アクセス方法 | this.name | this.#name |
デメリット | コンパイル時チェックなし、継承可能 | 一部の古いツールで認識されない可能性がある |
一般的には、プライベートクラスフィールドの方が、より強力で安全なアクセス制御を提供するため、privateキーワードよりも推奨されます。
ただし、古いツールとの互換性や、デバッグのしやすさなどを考慮する必要がある場合は、privateキーワードを使うことも検討できます。
- プライベートなメンバは、テストコードからでも直接アクセスすることはできません。
- テストコードでプライベートなメンバをテストするには、専用のアクセサーメソッドを用意する必要があります。
- プライベートなメンバは、クラスの外側からは一切アクセスできません。
- 例えば、別のクラスのインスタンスから
person.name
としてもアクセスできません。 - また、デバッガツールを使って直接値を確認することもできません。
- 例えば、別のクラスのインスタンスから
- プライベートなメンバは、インスタンスごとに個別に保持されます。
class Person {
private name: string;
constructor(name: string) {
this.name = name;
}
getName(): string {
return this.name;
}
}
const person = new Person('John Doe');
console.log(person.getName()); // Output: John Doe
// Accessing the private property directly will cause an error
console.log(person.name); // Error: Cannot access private property 'name'
Example 2: Using private class fields
class Person {
#name: string; // # symbol indicates a private class field
constructor(name: string) {
this.#name = name;
}
getName(): string {
return this.#name;
}
}
const person = new Person('Jane Doe');
console.log(person.getName()); // Output: Jane Doe
// Accessing the private class field directly works as expected
console.log(person.#name); // Output: Jane Doe
Example 3: Private class fields in action
class Point {
#x: number;
#y: number;
constructor(x: number, y: number) {
this.#x = x;
this.#y = y;
}
getDistanceFromOrigin(): number {
return Math.sqrt(this.#x * this.#x + this.#y * this.#y);
}
}
const point = new Point(3, 4);
console.log(point.getDistanceFromOrigin()); // Output: 5.0
// Cannot access private class fields directly outside of the class
console.log(point.#x); // Error: Cannot access private property '#x'
Key takeaways from the examples
- Private class fields offer better type safety and prevent accidental access from outside the class.
- Private class fields can be accessed directly within the class using the
#
symbol. - Private class fields use the
#
symbol to indicate a private member.
ゲッターとセッターは、プロパティの値を取得および設定するためのメソッドです。これらのメソッドを使用して、プライベートメンバーへのアクセスを制御できます。
class Person {
private name: string;
getName(): string {
return this.name;
}
setName(name: string): void {
this.name = name;
}
}
const person = new Person();
person.setName('John Doe');
console.log(person.getName()); // Output: John Doe
クラス内部のヘルパー関数
クラス内部で使用するヘルパー関数を定義し、その関数内でプライベートメンバーにアクセスする方法です。
class Person {
private name: string;
private getFullName(): string {
return `${this.name} Doe`;
}
getName(): string {
return this.name;
}
}
const person = new Person();
person.setName('Jane');
console.log(person.getName()); // Output: Jane
console.log(person.getFullName()); // Output: Jane Doe
デコレータ
デコレータを使用して、クラスのプロパティやメソッドの動作をカスタマイズできます。デコレータを使用して、プライベートメンバーへのアクセスを制御することもできます。
class Person {
private name: string;
@private
getName(): string {
return this.name;
}
}
const person = new Person();
person.setName('Peter');
console.log(person.getName()); // Output: Peter
注意事項
- プライベートメンバーへのアクセスを厳密に制御する必要がある場合は、private class fields を使用することを推奨します。
- これらの方法は、プライベートメンバーへのアクセスを完全にカプセル化することはできません。
- ゲッターとセッター、クラス内部のヘルパー関数、デコレータを使用する場合は、意図したアクセス制御が確実に実現されていることを確認する必要があります。
typescript class private