こんにちは。KOUKIです。
本記事では、TypeScriptを使って、クラスの基本概念や活用方法について記事にしています。
クラスの使い方について詳しくなれると思いますので、ぜひご一読ください。
学習まとめ
環境構築
以下の記事で作成したプロジェクトを使います。
※TypeScriptが動けば問題ありません
クラスとは
クラス(Class)は、オブジェクトの論理上の設計図になります。オブジェクト構造(プロパティやメソッド)を定義し、同じ構造の似たオブジェクトを繰り返し作成することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// クラスを定義 class Greeter { greeting: string; // プロパティ constructor(message: string) { // コンストラクタ this.greeting = message; } greet() { // メソッド return "Hello, " + this.greeting; } } // インスタンス化 let greeter = new Greeter("Hello"); let greeter2 = new Greeter("World"); |
TypeScriptのクラスの実装
クラスを作成しましょう。
classキーワード
クラスを作成するためには、「class」キーワードを使います。
1 |
class Japanese {} |
プロパティ
プロパティを定義する場合は、以下のようにします。
1 2 3 4 5 |
class Japanese { name: string; age: number; country: string; } |
それぞれのプロパティに設定されているstringやnumberはTypeScriptの型です。
型については、以下の記事をご覧ください。
初期値を設定することも可能です。
1 2 3 4 5 |
class Japanese { name: string; age: number; country: string = "Japanese"; // Japaneseを初期値として設定 } |

しかし、通常は、コンストラクタを使うのが一般的ですね。
コンストラクタ
コンストラクタは、インスタンス化した時に最初に呼び出されるメソッドです。オブジェクトの初期化処理を行います。
1 2 3 4 5 6 7 8 9 10 11 |
class Japanese { name: string; age: number; country: string; constructor(n: string, a: number, c: string) { this.name = n; this.age = a; this.country = c; } } |
インスタンス化
クラスを使えるようにするには、インスタンス化する必要があります。
クラスをインスタンス化するには、「new」キーワードを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class Japanese { name: string; age: number; country: string; constructor(n: string, a: number, c: string) { this.name = n; this.age = a; this.country = c; } } // インスタンス化 const selfnote = new Japanese('selfnote', 31, 'Japan') |
このインスタンス化により、selfnote変数にてクラスに対して操作を行うことができるようになります。
メソッドとthisキーワード
メソッドを定義する場合は、以下のようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
class Japanese { name: string; age: number; country: string; constructor(n: string, a: number, c: string) { this.name = n; this.age = a; this.country = c; } speakMyself() { console.log(`私は${this.name}です。歳は${this.age}で、${this.country}出身です。`); } } const selfnote = new Japanese('selfnote', 31, 'Japan'); selfnote.speakMyself(); |
メソッドとして、自己紹介メソッド(speakMyself)を定義しました。
このメソッドの内部では、「this」キーワードを指定しています。このthisは、クラスから作成されたインスタンスを参照します。上記の例では、selfnoteを参照することになりますね。
private修飾子とpublic修飾子
JavaやC#を学んだことがある人にとっては馴染み深いかもしれませんが、TypeScriptにもアクセス修飾子(privateやpublic)があります。
private修飾子は、外部からの一切の干渉を拒絶します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class Japanese { name: string; age: number; country: string; private valuables: string[] = []; // privateを設定 constructor(n: string, a: number, c: string) { this.name = n; this.age = a; this.country = c; } speakMyself() { console.log(`私は${this.name}です。歳は${this.age}で、${this.country}出身です。`); } } |
valuables(貴重品)をprivate修飾子付きで定義しました。これで、クラスの内部からでないとこのプロパティにはアクセスできません。
尚、アクセス修飾子は、メソッドにも定義可能です。
これが何が便利かというと、特定の操作方法を限定できたりします。
例えば、private修飾子を外して、valuablesプロパティを操作するメソッドを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class Japanese { name: string; age: number; country: string; valuables: string[] = []; constructor(n: string, a: number, c: string) { this.name = n; this.age = a; this.country = c; } speakMyself() { console.log(`私は${this.name}です。歳は${this.age}で、${this.country}出身です。`); } addValuable(v: string) { // 追加 this.valuables.push(v); } } const selfnote = new Japanese('selfnote', 31, 'Japan'); // 通常は、このように使用したい selfnote.addValuable("Watch"); // しかし、別の方法でも貴重品を追加できてしまう selfnote.valuables[1] = "Car"; |
本来であれば、addValuableメソッドの呼び出しで貴重品を追加したいところですが、プロパティの直接の呼び出しでも追加できてしまいます。
しかし、privateを設定した場合は、以下のようにエラーが発生します。


これは、結構便利です!
public修飾子はprivate修飾子とは逆で、クラスの外からアクセスすることが可能なアクセス修飾子です。アクセス修飾子をつけていないメソッドやプロパティはデフォルトでpublic修飾子がついた状態と同じになります。
プロパティ初期化のショートカット構文
コンストラクトを使うとプロパティの初期化の記述を短くすることができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class Japanese { private valuables: string[] = []; // ショートカット構文 // コンストラクタの引数にプロパティして定義したい引数をアクセス修飾子付きで定義する // publicは、明示的に指定しなければならない constructor( private name: string, private age: number, public country: string ) {} speakMyself() { console.log( `私は${this.name}です。歳は${this.age}で、${this.country}出身です。` ); } addValuable(v: string) { this.valuables.push(v); } } const selfnote = new Japanese("selfnote", 31, "Japan"); console.log(selfnote) |
1 2 3 4 |
Japanese {name: "selfnote", age: 31, country: "Japan", valuables: Array(0)} age: 31 country: "Japan" name: "selfnote" |
readonlyプロパティ
アクセス修飾子と関連して、「readonly」プロパティという読み取り専用にする修飾子があります。
1 |
private readonly hoge = 'test'; |
このように読み取り専用にしたいプロパティに対してreadonlyプロパティを設定すると値の書き換えが不可になります。もちろん、クラスの中からも変更できません。

「これは読み取り専用だぞ」と開発者の意図を伝えるのに便利そうですね。
クラスの継承
クラスは、他のクラスに継承させることができます。継承とは、派生クラスを作成する機能です。
Japaneseクラスを継承したTokyoTominクラスを定義しましょう。継承するには、「extends」キーワードを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class Japanese { constructor( private name: string, private age: number, public country: string ) {} speakMyself() { console.log( `私は${this.name}です。歳は${this.age}で、${this.country}出身です。` ); } } // Japaneseクラスを継承 class TokyoTomin extends Japanese {} const selfnote = new TokyoTomin("selfnote", 31, "Japan"); console.log(selfnote); |
上記のサンプルコードの通り、継承を行えば、継承元(Japanese)のプロパティやメソッドを引き継ぐことができます。それ故の派生クラスです。
派生クラスのコンストラクタ
派生クラスにコンストラクタを設定していない場合、インスタンス化時には継承元のコンストラクタが呼び出されます。
しかし、派生クラスにコンストラクタを設定すると派生クラスのコンストラクタが呼び出されます。その為、「super」キーワードを使って、継承元のコンストラクタを明示的に呼び出す必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class Japanese { constructor( private name: string, private age: number, public country: string ) {} speakMyself() { console.log( `私は${this.name}です。歳は${this.age}で、${this.country}出身です。` ); } } // Japaneseクラスを継承 class TokyoTomin extends Japanese { pol: string; // 県庁(都庁)所在地 constructor(name: string, age: number, pol: string) { // ベースクラスのコンストラクタを呼び出す // コンストラクタ内では、superを一番最初に書く必要がある super(name, age, "Japan"); this.pol = pol; } } const selfnote = new TokyoTomin("selfnote", 31, "Shinzyuku"); console.log(selfnote); |
protected修飾子
protected修飾子は、public修飾子やprivate修飾子と同じアクセス修飾子です。
これを使うと派生元のプロパティにアクセスすることができます。もちろんクラス外からはアクセスできません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
class Japanese { protected likeFood: string = "Cake"; // protectedを設定 constructor( private name: string, private age: number, public country: string ) {} speakMyself() { console.log( `私は${this.name}です。歳は${this.age}で、${this.country}出身です。` ); } } // Japaneseクラスを継承 class TokyoTomin extends Japanese { pol: string; constructor(name: string, age: number, pol: string) { super(name, age, "Japan"); this.pol = pol; } changeLikeFood(food: string) { this.likeFood = food; } } |
Getter & Setter
Getterは値を設定するメソッドで、Setterは値を設定するメソッドです。
これはTypeScriptの機能ではなく、モダンなJavaScriptでもサポートされています。
Getterを設定するには「get」キーワード、Setterを設定するには「set」キーワードをそれぞれ用います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// Japaneseクラスを継承 class TokyoTomin extends Japanese { private pol: string; get nowPol() { // getter return this.pol; } set nowPol(p: string) { // setter this.pol = p; } constructor(name: string, age: number, pol: string) { super(name, age, "Japan"); this.pol = pol; } changeLikeFood(food: string) { this.likeFood = food; } } const selfnote = new TokyoTomin("selfnote", 31, "Shinzyuku"); selfnote.nowPol = "Sibuya"; console.log(selfnote.nowPol); |
setterの使い方は少し混乱するかもしれません。
1 2 3 4 5 |
# OK selfnote.nowPol = "Sibuya"; # NG selfnote.nowPol("Sibuya"); |
staticキーワード
プロパティやメソッドには、「static」キーワードを付けることができます。
staticキーワードを付与したプロパティやメソッドは、インスタンス化しなくてもアクセスできるようになります。
1 2 3 4 5 6 7 8 9 |
class Book { static title = "Book Base"; static getTitle(title: string) { return title; } } console.log(Book.title); // => Book Base console.log(Book.getTitle("Harry Potter")); // => Harry Potter |
abstractキーワード
クラスやメソッドに「abstract」キーワードを付けると、それらを抽象化することができます。
先ほどのBookクラスをabstractキーワードを使って抽象化します。
1 2 3 |
abstract class Book { abstract getTitle(): string; } |
「抽象化」とあるように、abstractをつけたメソッドには、具体的な処理内容を記述することができません。
これの何が便利だと言いますと、継承先のクラスにgetTitleを強制的にオーバーライドすることを義務付けることができます。
1 2 3 4 5 6 7 |
abstract class Book { abstract getTitle(): string; } class HarryPotter implements Book { constructor(private title: string) {} } |
上記のコードでは、Bookを継承するHarryPotterクラスを定義しました。
しかし、以下のエラーメッセージが表示されます。

Class ‘HarryPotter’ incorrectly implements class ‘Book’. Did you mean to extend ‘Book’ and inherit its members as a subclass?
Property ‘getTitle’ is missing in type ‘HarryPotter’ but required in type ‘Book’.ts(2720)app.ts(44, 12): ‘getTitle’ is declared here.
このエラーを回避するために、HarryPotterクラスで、getTitleメソッドを定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
abstract class Book { abstract getTitle(): string; } class HarryPotter implements Book { constructor(private title: string) {} getTitle() { return this.title; } } const h = new HarryPotter("Harry Potter"); console.log(h.getTitle()); // => Harry Potter |
abstractは、絶対必須のメソッドを定義したいときに使用するのがいいと思います。また、継承先のメソッドごとに処理内容を変えることができるので、それも便利ですね。

ちなみにabstractをつけたクラスは、インスタンス化することができません。
シングルトンパターン
最後にシングルトンパターンを学びましょう。
シングルトンは、「クラスのインスタンスが必ず一つであることを保証する」デザインパターンの一つです。
TypeScriptに関わらず、PythonやGo言語でも実装することができます。
TypeScriptでシングルトンを実装する場合は、privateのコンストラクタを定義する必要があります。
シングルトンは、プログラム実行中に複数のインスタンスが作成されることが望ましくない時に使用します。
例えば、DBにアクセスするインスタンスを作成する際、プログラム実行毎にDBにアクセスできるインスタンスが作られるのではなく、既にインスタンスが存在している場合は、それを経由してDBにアクセスする方が競合などを発生させずに、安心して利用できます。
まずは、DBManagerクラスを作成して、privateなコンストラクタを定義します。
1 2 3 |
class DBManager { private constructor(private dbName: string, private pass: string) {} } |
privateでコンストラクタを定義すると、クラス外からインスタンス化ができなくなります。
1 2 3 4 |
const dbManager = new DBManager("mydb", "hoge"); // error Constructor of class 'DBManager' is private and only accessible within the class declaration. |
続いて、インスタンス化するためのプロパティとメソッドを「static」キーワード付きで宣言します。
1 2 3 4 5 |
class DBManager { private static instance: DBManager; // private constructor(private dbName: string, private pass: string) {} static getInstance() {} } |
instanceプロパティは、クラス内からのみアクセスできるstaticなメソッドです。また、getInstanceもstaticキーワード付きで定義しました。
getInstanceを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class DBManager { private static instance: DBManager; private constructor(private dbName: string, private pass: string) {} static getInstance() { if (DBManager.instance) { // インスタンスが存在している場合は、そのままインスタンスを返す return DBManager.instance; } else { // インスタンスが存在していない場合は、インスタンスを作成する this.instance = new DBManager("mydb", "hoge"); return this.instance; } } } |
インスタンスが存在しているかチェックして、存在している場合は、インスタンスをそのまま返却し、そうでない場合は、新規作成しています。
実際に同じインスタンスか確かめてみましょう。
1 2 3 4 |
const dbManager = DBManager.getInstance(); const dbManager2 = DBManager.getInstance(); console.log(dbManager === dbManager2); // => true |
取得したインスタンスを比較した結果、「true」が返却されたため、シングルトンの実装に成功したようです。
おわりに
TypeScriptのクラスの基本的な実装とその応用としてシングルトンパターンの実装を行いました。
覚えることが多くて大変ですが、実際に手を動かしながら実装すると、徐々に理解できるようになるはずです。
次は、TypeScriptのインターフェースを学びたいですね^^
それでは、また!
最近のコメント