【JavaScript/TypeScript】Classについて整理する
クラスの修飾子一覧と比較表
TypeScriptのクラスやそのメンバーには、以下の修飾子や記法を使用してアクセス制御や振る舞いを定義できます。
修飾子/記法 | 適用対象 | 説明 |
---|---|---|
public | プロパティ、メソッド、コンストラクタパラメータ | デフォルトのアクセス修飾子で、どこからでもアクセス可能。 |
private | プロパティ、メソッド | クラス内からのみアクセス可能。サブクラスや外部からはアクセス不可。 |
# (プライベートフィールド) | プロパティ | ECMAScriptの新しい記法で、クラス内からのみアクセス可能。ランタイムでも厳密にプライベート。 |
protected | プロパティ、メソッド | クラスおよびサブクラス内からアクセス可能。外部からはアクセス不可。 |
readonly | プロパティ | 初期化後に値を変更できないプロパティを定義。 |
static | プロパティ、メソッド | クラスのインスタンスではなく、クラス自体に関連付けられるメンバーを定義。 |
abstract | クラス、プロパティ、メソッド | 抽象クラスや抽象メソッドを定義。抽象クラスは直接インスタンス化できず、抽象メソッドは派生クラスで実装が必要。 |
修飾子の詳細と例
1. public
-
説明: デフォルトのアクセス修飾子で、クラス内外、サブクラス、他のモジュールからでもアクセス可能です。
-
例:
class Person { public name: string; public constructor(name: string) { this.name = name; } public greet() { console.log(`こんにちは、${this.name}です。`); } } const person = new Person("太郎"); person.greet(); // "こんにちは、太郎です。"
2. private
-
説明: クラス内からのみアクセス可能で、サブクラスや外部からはアクセスできません。ただし、
private
キーワードで定義されたメンバーは、コンパイル時のチェックのみであり、JavaScriptにトランスパイルされた後は完全なプライベートではありません。 -
例:
class Person { private age: number; constructor(age: number) { this.age = age; } getAge() { return this.age; } } const person = new Person(30); console.log(person.getAge()); // 30 // console.log(person.age); // エラー: 'age' は private です
3. # (プライベートフィールド)
-
説明: ECMAScriptのプライベートフィールド記法で、フィールド名の前に
#
を付けて定義します。これにより、ランタイムでも厳密にプライベートとなり、クラス外からはアクセスも参照もできません。 -
例:
class Person { #salary: number; constructor(salary: number) { this.#salary = salary; } getSalary() { return this.#salary; } } const person = new Person(5000); console.log(person.getSalary()); // 5000 // console.log(person.#salary); // エラー: プライベートフィールド '#salary' にアクセスできません
注意:
#
で始まるプライベートフィールドは、実際のJavaScriptコードでもプライベート性が維持されます。
4. protected
-
説明: クラスおよびサブクラス内からアクセス可能で、外部からはアクセスできません。
-
例:
class Person { protected gender: string; constructor(gender: string) { this.gender = gender; } } class Employee extends Person { getGender() { return this.gender; } } const employee = new Employee("男性"); console.log(employee.getGender()); // "男性" // console.log(employee.gender); // エラー: 'gender' は protected です
5. readonly
-
説明: 初期化時にのみ値を設定でき、それ以降は変更できないプロパティを定義します。
-
例:
class Person { readonly birthDate: Date; constructor(birthDate: Date) { this.birthDate = birthDate; } } const person = new Person(new Date(1990, 1, 1)); console.log(person.birthDate); // 出力: 1990-02-01T00:00:00.000Z // person.birthDate = new Date(); // エラー: 'birthDate' は読み取り専用です
6. static
-
説明: クラスのインスタンスではなく、クラス自体に関連付けられるメンバーを定義します。
-
例:
class MathUtil { static PI: number = 3.14; static circleArea(radius: number): number { return MathUtil.PI * radius * radius; } } console.log(MathUtil.PI); // 3.14 console.log(MathUtil.circleArea(10)); // 314
7. abstract
-
説明: 抽象クラスや抽象メソッドを定義します。抽象クラスは直接インスタンス化できず、抽象メソッドは派生クラスで実装が必要です。
-
例:
abstract class Animal { abstract makeSound(): void; move(): void { console.log("移動します。"); } } class Dog extends Animal { makeSound() { console.log("ワンワン"); } } const dog = new Dog(); dog.makeSound(); // "ワンワン" dog.move(); // "移動します。"
まとめ
これらの修飾子や記法を適切に使用することで、クラスやそのメンバーのアクセス制御や動作を明確に定義できます。
特に、#
を使用したプライベートフィールドは、ランタイムレベルでの完全なプライバシーを提供します。
開発者間でのコードの理解を深め、バグの発生を防ぐのに役立ちます。
private
と#
の違い
補足: -
private
キーワード: TypeScriptのコンパイル時にのみチェックされ、JavaScriptにトランスパイルされた後は通常のプロパティになります。 -
#
プライベートフィールド: ECMAScriptの仕様に基づき、ランタイムでも厳密にプライベートが保証されます。クラス外からは一切アクセスできず、リフレクションやプロキシを使っても取得できません。
選択の指針:
- ランタイムでもプライベート性を保ちたい場合は、
#
を使用します。 - コンパイル時のチェックのみで十分な場合や、既存のコードベースとの互換性を考慮する場合は、
private
キーワードを使用します。
private
vs #
例: class Example {
private privateValue = "private";
#privateField = "private field";
getPrivateValue() {
return this.privateValue;
}
getPrivateField() {
return this.#privateField;
}
}
const example = new Example();
console.log(example.getPrivateValue()); // "private"
console.log(example.getPrivateField()); // "private field"
// 以下はエラーになります
// console.log(example.privateValue); // エラー: 'privateValue' は private です
// console.log(example.#privateField); // エラー: プライベートフィールド '#privateField' にアクセスできません
TypeScriptにおけるabstract class(抽象クラス)
TypeScriptにおける**abstract class(抽象クラス)**とは、直接インスタンス化できないクラスであり、他のクラスによって継承されることを目的としています。
抽象クラスは、実装のない抽象メソッドを含むことができ、これらのメソッドは派生クラスで具体的に実装される必要があります。
抽象クラスの例
abstract class Animal {
abstract makeSound(): void; // 抽象メソッド
move(): void {
console.log("移動する");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("ワンワン");
}
}
const dog = new Dog();
dog.makeSound(); // "ワンワン"
dog.move(); // "移動する"
// 以下はエラーとなる
// const animal = new Animal(); // エラー: 抽象クラスはインスタンス化できません
private constructorと通常の constructor の違い