クラスの定義
class User {
// プロパティの宣言
name: string;
age: number;
// コンストラクタ
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// メソッド
greet(): string {
return `こんにちは、${this.name}です`;
}
}
const user = new User("太郎", 25);
console.log(user.greet()); // "こんにちは、太郎です"
アクセス修飾子
public, private, protected
class Person {
public name: string; // どこからでもアクセス可能(デフォルト)
private age: number; // クラス内からのみ
protected id: number; // クラスとサブクラスから
readonly country: string; // 読み取り専用
constructor(name: string, age: number, id: number) {
this.name = name;
this.age = age;
this.id = id;
this.country = "Japan";
}
getAge(): number {
return this.age; // クラス内からはアクセス可能
}
}
const person = new Person("太郎", 25, 1);
console.log(person.name); // OK
// console.log(person.age); // エラー:private
// console.log(person.id); // エラー:protected
// person.country = "USA"; // エラー:readonly
パラメータプロパティ
コンストラクタ引数にアクセス修飾子を付けると、自動的にプロパティになります。
class User {
// 簡潔な書き方
constructor(
public name: string,
private age: number,
readonly id: number
) {}
getAge(): number {
return this.age;
}
}
// 上記は以下と同等
class UserVerbose {
public name: string;
private age: number;
readonly id: number;
constructor(name: string, age: number, id: number) {
this.name = name;
this.age = age;
this.id = id;
}
}
プロパティ
オプショナルプロパティ
class User {
name: string;
email?: string; // オプショナル
constructor(name: string, email?: string) {
this.name = name;
this.email = email;
}
}
const user1 = new User("太郎");
const user2 = new User("花子", "hanako@example.com");
プロパティ初期化子
class Config {
debug: boolean = false;
maxItems: number = 100;
theme: string = "light";
constructor(options?: Partial<Config>) {
Object.assign(this, options);
}
}
const config = new Config({ debug: true });
console.log(config.debug); // true
console.log(config.maxItems); // 100
ゲッターとセッター
class Circle {
private _radius: number;
constructor(radius: number) {
this._radius = radius;
}
// ゲッター
get radius(): number {
return this._radius;
}
// セッター
set radius(value: number) {
if (value < 0) {
throw new Error("半径は0以上である必要があります");
}
this._radius = value;
}
// 計算プロパティ(ゲッターのみ)
get area(): number {
return Math.PI * this._radius ** 2;
}
}
const circle = new Circle(5);
console.log(circle.radius); // 5
console.log(circle.area); // 78.54...
circle.radius = 10;
// circle.radius = -1; // エラー
静的メンバー
class MathUtils {
static readonly PI = 3.14159;
private static instanceCount = 0;
constructor() {
MathUtils.instanceCount++;
}
static add(a: number, b: number): number {
return a + b;
}
static getInstanceCount(): number {
return MathUtils.instanceCount;
}
}
console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.add(1, 2)); // 3
new MathUtils();
new MathUtils();
console.log(MathUtils.getInstanceCount()); // 2
静的ブロック
class Database {
static connection: string;
static {
// 静的初期化ブロック
console.log("Databaseクラスの初期化");
Database.connection = "localhost:5432";
}
}
抽象クラス
インスタンス化できない基底クラスです。
abstract class Shape {
constructor(public name: string) {}
// 抽象メソッド(サブクラスで実装必須)
abstract area(): number;
abstract perimeter(): number;
// 通常のメソッド
describe(): string {
return `${this.name}: 面積=${this.area()}, 周囲=${this.perimeter()}`;
}
}
class Rectangle extends Shape {
constructor(
public width: number,
public height: number
) {
super("長方形");
}
area(): number {
return this.width * this.height;
}
perimeter(): number {
return 2 * (this.width + this.height);
}
}
class Circle extends Shape {
constructor(public radius: number) {
super("円");
}
area(): number {
return Math.PI * this.radius ** 2;
}
perimeter(): number {
return 2 * Math.PI * this.radius;
}
}
// const shape = new Shape("形"); // エラー:抽象クラスはインスタンス化不可
const rect = new Rectangle(10, 5);
console.log(rect.describe());
クラス式
// 無名クラス式
const User = class {
constructor(public name: string) {}
};
// 名前付きクラス式
const Product = class ProductClass {
constructor(public name: string, public price: number) {}
};
const user = new User("太郎");
const product = new Product("本", 1500);
thisの型
class Builder {
private value: string = "";
append(text: string): this {
this.value += text;
return this; // メソッドチェーン用
}
build(): string {
return this.value;
}
}
const result = new Builder()
.append("Hello, ")
.append("World!")
.build();
console.log(result); // "Hello, World!"
実践例:イベントエミッター
type EventHandler<T = void> = (data: T) => void;
class EventEmitter<Events extends Record<string, unknown>> {
private handlers = new Map<keyof Events, Set<EventHandler<unknown>>>();
on<K extends keyof Events>(
event: K,
handler: EventHandler<Events[K]>
): void {
if (!this.handlers.has(event)) {
this.handlers.set(event, new Set());
}
this.handlers.get(event)!.add(handler as EventHandler<unknown>);
}
off<K extends keyof Events>(
event: K,
handler: EventHandler<Events[K]>
): void {
this.handlers.get(event)?.delete(handler as EventHandler<unknown>);
}
emit<K extends keyof Events>(event: K, data: Events[K]): void {
this.handlers.get(event)?.forEach(handler => handler(data));
}
}
// 使用例
interface AppEvents {
login: { userId: number; timestamp: Date };
logout: void;
error: Error;
}
const emitter = new EventEmitter<AppEvents>();
emitter.on("login", (data) => {
console.log(`User ${data.userId} logged in`);
});
emitter.on("error", (error) => {
console.error(error.message);
});
emitter.emit("login", { userId: 1, timestamp: new Date() });
実践例:シングルトン
class Database {
private static instance: Database;
private connected: boolean = false;
private constructor() {
// privateコンストラクタ
}
static getInstance(): Database {
if (!Database.instance) {
Database.instance = new Database();
}
return Database.instance;
}
connect(): void {
this.connected = true;
console.log("データベースに接続しました");
}
isConnected(): boolean {
return this.connected;
}
}
const db1 = Database.getInstance();
const db2 = Database.getInstance();
console.log(db1 === db2); // true(同じインスタンス)
まとめ
public,private,protectedでアクセス制御- パラメータプロパティで簡潔に記述
readonlyで読み取り専用プロパティget/setでゲッター・セッターstaticでクラスレベルのメンバーabstractで抽象クラス・抽象メソッドthisの型でメソッドチェーン
次回は継承とインターフェースについて学びます。