【TypeScript】privateの12の実用例をプロが解説

初心者向けTypeScriptのprivate修飾子の詳細解説TypeScript
この記事は約27分で読めます。

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればサンプルコードを活用して機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

TypeScriptの特徴的な機能のひとつ、private修飾子。あなたもTypeScriptを学び始めると必ずと言っていいほど遭遇するでしょう。

しかし、このprivate修飾子の真価は、正しく使いこなすことで初めて発揮されます。

この記事では、TypeScriptでのprivateの具体的な使い方やそのメリットを、具体的なサンプルコードとともに初心者向けに解説します。

記事の最後まで読んで、TypeScriptのprivate修飾子を完全に理解し、あなたのコードに活用してみましょう。

●TypeScriptとは

TypeScriptとは、開発者の助けとなるよう設計されたプログラミング言語です。

コードの品質管理からバグの削減、チームでの開発効率の向上に至るまで、様々なメリットを提供します。

明確な型システムを通じて、コードベースをより強固にし、効率的な開発プロセスを支えるTypeScriptは、今日の開発現場で欠かせないツールの一つです。

次に、TypeScriptを取り巻く環境とその目的、そしてJavaScriptとの比較について具体的に見ていきましょう。

○TypeScriptの概要

TypeScriptは、Microsoftが開発した静的型付け言語です。

JavaScriptのスーパーセットとして設計されており、JavaScriptのすべての機能を持ちつつ、静的型チェックやクラスベースのオブジェクト指向プログラミングなどの追加機能を備えています。

これにより、大規模なプロジェクトでも安全に、そして効率的にコードを管理することが可能となりました。

特に、TypeScriptは型安全性を強調しています。

型エラーは、コンパイル時に検出されるため、ランタイムエラーのリスクを大幅に削減することができます。

また、TypeScriptは豊富なIDEのサポートを受けており、VSCodeやWebStormなどのエディタでの自動補完やリファクタリングが容易となっています。

○TypeScriptとJavaScriptの違い

JavaScriptとTypeScript、似て非なる二つの言語。

その最大の違いは「型」にあります。JavaScriptは動的型付け言語で、変数の型は実行時に解釈されます。

一方、TypeScriptは先述した通り静的型付け言語で、変数の型はコードを書く段階で指定することが求められます。

例えば、JavaScriptで次のようなコードを書きます。

let number = 10;
number = "文字列";

このコードはJavaScriptでは問題なく動作しますが、TypeScriptではエラーとなります。

なぜなら、numberという変数に数値を割り当てた後で文字列を割り当てようとしているからです。

また、TypeScriptはインターフェースやジェネリクスといった高度な型システムの機能を提供しています。

これにより、より柔軟かつ堅牢なコード設計が可能となります。

さらに、TypeScriptはモジュールシステムをサポートしています。

JavaScriptのES6モジュールに加え、CommonJSやAMDなどのモジュールフォーマットにも対応しています。

●private修飾子の基本

TypeScriptでクラスやオブジェクトを定義する際、その中のメソッドやプロパティのアクセスレベルを制限するためにprivate修飾子を使用します。

これにより、外部からの直接的なアクセスを禁止し、クラス内部だけで使用することができます。

ここでは、private修飾子の基本的な概念について、その役割と用途を詳細に解説します。

○privateとは何か?

TypeScriptでは、クラスやインターフェースのメンバー(プロパティやメソッドなど)のアクセスレベルを指定するための修飾子がいくつか提供されています。

その中でも、privateは最も厳格なアクセスレベルを持つ修飾子となります。

具体的には、private修飾子を付けたメンバーは、そのクラスの内部からのみアクセス可能となります。

外部からそのメンバーを直接呼び出すことはできません。

これにより、クラスの実装の詳細を隠蔽し、クラスの安全性を高めることが可能となります。

例えば、ある変数の値を外部から変更させたくない場合や、特定のメソッドを外部から呼び出せないようにしたい場合にprivate修飾子を使用します。

○private修飾子の役割

private修飾子の主な役割は、クラスの内部状態や実装の詳細を保護することです。

この修飾子を使用することで、外部からアクセスされるべきでない部分を隠蔽することができます。

□情報の隠蔽

クラスの内部構造やロジックを隠蔽することで、外部からの不正な操作や不要な情報へのアクセスを防ぐことができます。

これにより、クラスの安定性や信頼性を保つことが可能となります。

□保守性の向上

クラスの詳細を隠蔽することで、将来的な変更や拡張が容易になります。

外部から直接アクセスできない部分は、内部の変更が他の部分に影響を及ぼすリスクを低減させることができます。

□安全性の確保

private修飾子を使用することで、外部から特定のメソッドやプロパティにアクセスすることを制限できます。

これにより、外部からの不正な操作を防ぐことができます。

●privateの使い方

TypeScriptには、変数や関数、クラスメソッドに対するアクセスレベルを制限するための機能が提供されています。

この中でも、最も厳格なアクセスレベルを持つのが「private」修飾子です。

ここでは、TypeScriptでのprivateの基本的な使い方に焦点を当て、その機能と役割をサンプルコードを交えながら詳しく解説していきます。

○サンプルコード1:クラス内でのprivateの基本的な使い方

TypeScriptのクラス内でprivate修飾子を使用することで、そのプロパティやメソッドはクラス外からアクセスすることができなくなります。

これにより、クラスの内部状態や動作を外部から隠蔽し、安全なコード設計を実現することができます。

class Person {
    private name: string;

    constructor(name: string) {
        this.name = name;
    }

    greet(): string {
        return "こんにちは、" + this.name + "さん";
    }
}

const taro = new Person("太郎");
// taro.nameはアクセス不可能
console.log(taro.greet()); // "こんにちは、太郎さん"を出力

このコードでは、Personクラスにnameというprivateプロパティを持っています。

このプロパティはクラスの外部から直接アクセスすることができません。

そのため、taro.nameのような形でアクセスしようとするとコンパイルエラーが発生します。

しかし、クラス内部のメソッド(この場合はgreetメソッド)からはprivateプロパティにアクセスすることができます。

これにより、greetメソッドを通じて間接的にnameプロパティの値を取得することが可能となります。

このコードを実行すると、コンソールには”こんにちは、太郎さん”というメッセージが表示されます。

このように、private修飾子を使用することで、クラスの内部実装を隠蔽し、クラスを安全に使用することができます。

○サンプルコード2:privateなメソッドの作成と呼び出し

TypeScriptでは、メソッドやプロパティをprivateとして定義することで、そのクラスの外部からのアクセスを制限することができます。

これにより、意図しない操作や不正なアクセスからデータを保護することが可能となります。

privateなメソッドの作成とその呼び出し方法に関するサンプルコードを紹介します。

class Greeting {
    private message: string;

    constructor(message: string) {
        this.message = message;
    }

    public sayHello(): string {
        return this.getGreetingMessage();
    }

    private getGreetingMessage(): string {
        return "Hello, " + this.message + "!";
    }
}

let greet = new Greeting("TypeScript");
console.log(greet.sayHello());

このコードでは、Greetingというクラスを定義しています。

このクラスにはmessageというprivateなプロパティと、getGreetingMessageというprivateなメソッドが存在します。

sayHelloメソッドはpublicなメソッドで、この中からprivateなgetGreetingMessageメソッドを呼び出しています。

このように、クラス内部の別のメソッドからはprivateなメソッドを自由に呼び出すことができます。

しかし、クラスの外部からはgetGreetingMessageメソッドを直接呼び出すことはできません。

このコードを実行すると、Hello, TypeScript!というメッセージが出力されます。

これは、sayHelloメソッドがprivateなgetGreetingMessageメソッドを通じてmessageプロパティの値にアクセスし、その結果を返しているからです。

○サンプルコード3:privateなプロパティとそのアクセス方法

TypeScriptを使ってプログラムを記述する際、オブジェクト指向の特性を生かすためにはアクセス修飾子を理解することが不可欠です。

今回はその中でも、privateという修飾子の使い方について焦点を当てて解説します。

まず、私たちはprivateなプロパティとは何かを理解する必要があります。

private修飾子を持つプロパティやメソッドは、そのクラスの外部からはアクセスすることができません。

これは、クラスの内部構造やロジックを隠蔽し、外部からの不正なアクセスや変更を防ぐために非常に有効です。

具体的なサンプルコードとともにこの概念を詳しく見ていきましょう。

class User {
    private password: string;

    constructor(password: string) {
        this.password = password;
    }

    // パスワードをチェックするメソッド
    public isValidPassword(inputPassword: string): boolean {
        return this.password === inputPassword;
    }
}

このコードでは、Userというクラスを定義しています。

このクラスの中にはpasswordというprivateなプロパティが存在し、外部からは直接アクセスすることができません。

しかし、privateなプロパティにアクセスするための公開されたメソッドisValidPasswordを定義することで、特定の操作のみ外部から許可されるようになります。

このメソッドは、入力されたパスワードが正しいかどうかをチェックするためのものです。

このコードを実行すると、外部からはpasswordに直接アクセスすることはできませんが、isValidPasswordメソッドを通じてパスワードが正しいかどうかの判定は行うことができます。

例えば次のようなコードを考えてみましょう。

const user = new User("secretPassword");
console.log(user.isValidPassword("testPassword")); // false
console.log(user.isValidPassword("secretPassword")); // true

こちらのコードは、Userクラスのインスタンスを生成し、isValidPasswordメソッドを使ってパスワードが正しいかどうかを判定しています。

結果として、最初の判定ではfalseが、次の判定ではtrueが出力されます。

○サンプルコード4:コンストラクタでのprivateの使用方法

TypeScriptでは、クラスのコンストラクタ内でprivate修飾子を使用することで、一般的なプロパティ宣言と初期化を同時に行うことができます。

この手法を利用すると、コードの簡潔化が図れますし、意図しないプロパティの外部からのアクセスを避けることもできます。

具体的には、次のようにコンストラクタの引数にprivate修飾子を付与することで、そのクラスのprivateなプロパティとして自動的に宣言および初期化されます。

class Person {
    // コンストラクタ内でprivate修飾子を用いてプロパティの宣言と初期化を行う
    constructor(private name: string, private age: number) {}

    // このクラス内ではnameとageにアクセス可能
    introduce(): string {
        return `私の名前は${this.name}で、年齢は${this.age}歳です。`;
    }
}

const tanaka = new Person('田中', 25);
console.log(tanaka.introduce()); // "私の名前は田中で、年齢は25歳です。"と表示される

このコードではPersonクラスを宣言しています。

そのコンストラクタの引数には、private修飾子を用いてnameageプロパティを宣言し、同時に初期化しています。

そのため、コンストラクタ内で別途プロパティの宣言や初期化を行う必要がなく、コードがスッキリとします。

また、この方法を使用すると、自動的に生成されるプロパティはprivate修飾子が付与されているため、そのクラスの外部からアクセスすることができません。

例えば、上記のコードでtanaka.nametanaka.ageに直接アクセスしようとすると、コンパイルエラーが発生します。

このコードを実行すると、tanaka.introduce()の結果、”私の名前は田中で、年齢は25歳です。”という文字列が表示されます。

このintroduceメソッド内では、privateプロパティのnameageにアクセスすることができます。

●privateの応用例

TypeScriptでは、private修飾子を用いることで、特定のプロパティやメソッドのスコープを限定することができます。

しかし、基本的な使用方法だけでなく、この修飾子はさまざまな応用的な方法で利用することができます。

ここでは、特にカプセル化の概念を取り入れたprivateの応用例について解説します。

○サンプルコード5:private修飾子を使ったカプセル化

カプセル化はオブジェクト指向プログラミングの主要な概念の一つで、内部の実装詳細を隠蔽し、外部から直接アクセスできないようにすることを意味します。

TypeScriptのprivate修飾子を使用することで、このカプセル化を簡単に実現することができます。

銀行口座を模倣したクラスのサンプルコードを紹介します。

このクラスでは、口座の残高を示すbalanceプロパティをprivateでカプセル化しています。

class BankAccount {
    private balance: number;

    constructor(initialAmount: number) {
        this.balance = initialAmount;
    }

    deposit(amount: number): void {
        this.balance += amount;
    }

    withdraw(amount: number): boolean {
        if (this.balance >= amount) {
            this.balance -= amount;
            return true;
        } else {
            console.log("残高不足です。");
            return false;
        }
    }

    checkBalance(): number {
        return this.balance;
    }
}

const myAccount = new BankAccount(10000);
myAccount.deposit(5000);
console.log(myAccount.checkBalance()); // 15000
myAccount.withdraw(3000);
console.log(myAccount.checkBalance()); // 12000

このコードでは、BankAccountクラスにはbalanceというprivateなプロパティがあります。

このプロパティは、クラスの外部から直接アクセスすることができないため、クラスのメソッドを通してのみ操作することができます。

具体的には、預金をする際にはdepositメソッド、引き出しをする際にはwithdrawメソッドを使用します。

また、現在の残高を確認するにはcheckBalanceメソッドを用います。

このコードを実行すると、最初に10000円の預金がある状態から、5000円を預けた後、3000円を引き出す流れとなります。

その結果、最後の残高は12000円となります。

○サンプルコード6:privateとgetter/setterの組み合わせ

TypeScriptでのオブジェクト指向プログラミングの際、データのカプセル化は非常に重要な役割を果たします。

これは、クラス内のデータを外部から直接触れないようにして、クラスの内部状態を保護するためのものです。

そのカプセル化を実現するための手段として、private修飾子とgetter/setterの組み合わせが非常に効果的です。

TypeScriptでprivate修飾子とgetter/setterを組み合わせたサンプルコードを紹介します。

class Person {
    // privateなプロパティの宣言
    private _name: string;

    constructor(name: string) {
        this._name = name;
    }

    // nameのgetter
    get name(): string {
        return this._name;
    }

    // nameのsetter
    set name(value: string) {
        if (value.length > 0) {
            this._name = value;
        } else {
            console.log('名前は1文字以上で設定してください。');
        }
    }
}

const person = new Person('太郎');
console.log(person.name);  // 太郎を出力
person.name = '花子';
console.log(person.name);  // 花子を出力
person.name = '';  // 名前は1文字以上で設定してください。と警告

このコードでは、Personクラスに_nameというprivateなプロパティを持ち、そのアクセッサ(getterとsetter)を定義しています。

getterはプロパティの値を取得するためのもので、setterはプロパティの値を設定するためのものです。

setter内部では、名前が1文字以上であることをチェックしており、それを満たさない場合は警告を出力します。

このコードを実行すると、まずPersonのインスタンスが作成され、「太郎」という名前が_nameプロパティに設定されます。

次に、person.nameを出力することで、getterが呼び出され、_nameの値が取得され、「太郎」という文字列が出力されます。

○サンプルコード7:privateとstaticを組み合わせた例

TypeScriptの中でprivate修飾子とstaticキーワードを組み合わせると、クラスレベルでのプライベートな静的変数やメソッドを定義することができます。

これは、そのクラス自体のみでアクセス可能で、インスタンスからはアクセスすることができません。

まず、次のコードはprivatestaticを組み合わせた基本的な使用方法を表しています。

class MyClass {
    // privateな静的変数
    private static count = 0;

    // privateな静的メソッド
    private static displayCount() {
        console.log(`現在のcountは${this.count}です。`);
    }

    // クラスのインスタンスを生成するたびにcountをインクリメントする
    constructor() {
        MyClass.count++;
    }

    // publicなメソッドからprivateな静的メソッドを呼び出す
    public showCount() {
        MyClass.displayCount();
    }
}

// インスタンスを2つ作成
const obj1 = new MyClass();
const obj2 = new MyClass();

// countの値を表示
obj1.showCount();  // 現在のcountは2です。

このコードでは、MyClassというクラスを定義しています。

countというprivateな静的変数と、それを表示するdisplayCountというprivateな静的メソッドを持っています。

コンストラクタは、インスタンスが生成されるたびにcountをインクリメントします。

そして、showCountというpublicなメソッドを使って、displayCountメソッドを呼び出します。

このコードを実行すると、2つのインスタンスを生成した後、showCountメソッドを呼び出すと、「現在のcountは2です。」という結果が表示されます。

これは、2つのインスタンスが生成されたため、countが2回インクリメントされた結果です。

重要なポイントは、countdisplayCountのようなprivateな静的メンバーは、そのクラスの外からアクセスすることができないということです。

ただし、クラス内のpublicなメソッドからはアクセスできるため、外部からのアクセスを制限しながら、特定の処理を内部で行うことが可能です。

○サンプルコード8:privateとreadonlyの組み合わせ

TypeScriptのprivate修飾子は、メンバーのアクセスを制限するためのキーワードです。

一方、readonlyは変数の再代入を防ぐためのキーワードです。

これらを組み合わせることで、安全で読み取り専用のプライベートメンバーをクラス内で定義することができます。

class Person {
    private readonly name: string;

    constructor(name: string) {
        this.name = name;
    }

    introduceYourself() {
        return `私の名前は${this.name}です。`;
    }
}

const person1 = new Person("太郎");
console.log(person1.introduceYourself());
// person1.name = "花子"; // これはエラーになります

このコードでは、Personというクラスが定義されています。

その中で、private readonlynameというプロパティを定義しています。

これにより、nameはクラス外からアクセスできないだけでなく、クラス内からも再代入が禁止されています。

introduceYourselfというメソッドを用いて、その人の名前を紹介する文字列を返すことができます。

このメソッド内ではnameプロパティにアクセスしていますが、nameの再代入は許されていません。

実際に、const person1 = new Person("太郎");Personクラスのインスタンスを生成しています。

このインスタンスを使ってintroduceYourselfメソッドを呼び出すと、私の名前は太郎です。という結果が得られます。

●注意点と対処法

○privateの使用時の一般的なエラーとその解決策

TypeScriptのprivate修飾子を使用する際には、いくつかの一般的なエラーや注意点があります。

ここでは、それらのエラーや注意点と、それらを解決するための方法を詳しく解説します。

□private変数への外部からのアクセス

この問題は、最も一般的なエラーの一つです。

クラス外部からprivate修飾子で宣言されたメンバにアクセスしようとすると、コンパイルエラーが発生します。

class SampleClass {
    private sampleVar: string = "private変数";
}

let obj = new SampleClass();
console.log(obj.sampleVar);  // コンパイルエラー

このコードでは、sampleVarprivateとして宣言されているため、クラスの外部からアクセスしようとするとエラーが発生します。

対処法として、privateな変数やメソッドにアクセスする必要がある場合は、そのクラス内でpublicなメソッドを用意して、そのメソッド経由でアクセスするようにします。

class SampleClass {
    private sampleVar: string = "private変数";
    public getSampleVar(): string {
        return this.sampleVar;
    }
}

let obj = new SampleClass();
console.log(obj.getSampleVar());  // "private変数"と出力される

□派生クラスでのprivate変数のアクセス

派生クラス(サブクラス)から、基底クラス(スーパークラス)のprivate変数やメソッドにアクセスしようとすると、やはりコンパイルエラーが発生します。

class BaseClass {
    private baseVar: string = "基底クラスの変数";
}

class DerivedClass extends BaseClass {
    showVar() {
        console.log(this.baseVar);  // コンパイルエラー
    }
}

このコードでは、DerivedClassBaseClassを継承していますが、baseVarprivateとして宣言されているため、派生クラスからアクセスすることはできません。

対処法として、基底クラスの変数やメソッドを派生クラスからアクセスしたい場合は、protected修飾子を使用します。

これにより、基底クラス自身と派生クラスからはアクセスできるようになりますが、クラスの外部からはアクセスできなくなります。

class BaseClass {
    protected baseVar: string = "基底クラスの変数";
}

class DerivedClass extends BaseClass {
    showVar() {
        console.log(this.baseVar);  // "基底クラスの変数"と出力される
    }
}

○派生クラスでのprivateの取り扱い

TypeScriptでは、派生クラスで基底クラスのprivate変数やメソッドをオーバーライドすることはできません。

したがって、このような場合もコンパイルエラーが発生します。

class BaseClass {
    private baseMethod(): void {
        console.log("基底クラスのメソッド");
    }
}

class DerivedClass extends BaseClass {
    private baseMethod(): void {  // コンパイルエラー
        console.log("派生クラスのメソッド");
    }
}

このコードでは、DerivedClassBaseClassbaseMethodをオーバーライドしようとしていますが、baseMethodprivateとして宣言されているため、エラーが発生します。

対処法として、基底クラスのメソッドを派生クラスでオーバーライドする必要がある場合、基底クラスでのメソッドのアクセス修飾子をprotectedに変更します。

これにより、派生クラスでのオーバーライドが可能となります。

class BaseClass {
    protected baseMethod(): void {
        console.log("基底クラスのメソッド");
    }
}

class DerivedClass extends BaseClass {
    protected baseMethod(): void {
        console.log("派生クラスのメソッド");
    }
}

●カスタマイズ方法

TypeScriptでのプログラミングでは、private修飾子の使い方や役割を理解するだけでなく、それをさらにカスタマイズして効果的に使用することも求められることがあります。

ここでは、privateの代わりに使用できる他の修飾子や、より拡張性を持たせたprivateの設計方法について詳しく解説していきます。

○privateの代わりに使用できる他の修飾子

□protected修飾子

この修飾子を使用すると、そのクラス自体と派生クラスからアクセスすることができるようになります。

一方で、クラスの外からはアクセスできません。

この特性を利用して、派生クラスでも利用したいが、外部からは利用したくないプロパティやメソッドを定義する際に使用します。

class BaseClass {
    protected protectedProperty: string;

    constructor() {
        this.protectedProperty = "protected";
    }
}

class DerivedClass extends BaseClass {
    show() {
        // 派生クラスからはアクセス可能
        console.log(this.protectedProperty);
    }
}

let obj = new DerivedClass();
obj.show(); // protected

このコードではBaseClass内のprotectedPropertyに対して、DerivedClassからはアクセスすることができます。

一方で、直接obj.protectedPropertyのようにアクセスすることはできません。

このコードを実行すると、”protected”と表示されます。

□public修飾子

この修飾子はデフォルトでクラスのメンバーに適用されています。

publicが明示的についている場合、そのメンバーはどこからでも自由にアクセスすることができます。

□readonly修飾子

この修飾子はプロパティの値が代入された後、再度代入することができないことを表します。

コンストラクタ内では代入が可能ですが、それ以外の場所では変更することはできません。

○拡張性を持たせたprivateの設計方法

private修飾子を使うことでカプセル化を実現することができますが、時には柔軟性や拡張性を持たせることが求められることもあります。

そこで、次のような方法を取ることで、拡張性を持たせつつprivateの性質を保持することができます。

□getter/setterを活用

privateプロパティの値を外部から取得したり、設定したりするためにgetterやsetterを使用します。

これにより、アクセス方法や条件をカスタマイズできます。

class MyClass {
    private _value: number;

    constructor(value: number) {
        this._value = value;
    }

    // getter
    get value(): number {
        return this._value;
    }

    // setter
    set value(newValue: number) {
        if (newValue >= 0) {
            this._value = newValue;
        }
    }
}

let obj = new MyClass(5);
console.log(obj.value); // 5
obj.value = 3;
console.log(obj.value); // 3
obj.value = -1;
console.log(obj.value); // 3(変更されていない)

このコードでは、_valueというprivateプロパティの値をgetterとsetterを用いて外部からアクセスする例を表しています。

setter内での条件により、値が0未満の場合には代入が行われないようになっています。

このコードを実行すると、”5″, “3”, “3”と順に表示されます。

まとめ

TypeScriptのprivate修飾子は、クラス内の変数やメソッドに対するアクセス制御を強化するための強力なツールです。

この記事では、private修飾子の基本的な使い方から応用例、注意点、そしてカスタマイズ方法までを詳しく解説しました。

TypeScriptを使用する際には、private修飾子の知識は不可欠です。

この記事を通じて、その強力な機能を十分に活用し、品質の高いコードを書く力を身につけることを願っています。

今後の開発に、ぜひともお役立てください。