- TypeScript
TypeScript class書き方|private・readonly・継承
TypeScriptのclassは、JavaScriptのclass構文に「プロパティの型」と「public・protected・private・readonlyなどの修飾子」を足したもので、どこから読み書きできるかをコンパイル時に確かめられます。 書き方そのものは短く覚えられますが、修飾子が7種類あり、「サブクラスから読めるのはどれか」「実行時にも隠れるのはどれか」が混ざりやすいところです。
この記事では、7つの修飾子を「クラス内・サブクラス・外部・実行時」の4つの視点で比べたアクセス可否表を最初に置き、それぞれの違反で出るエラー文(TS2341、TS2445、TS18013 など)を TypeScript 7.0.2 で実際に再現しました。あわせて、private と #private がコンパイル後の JavaScript でどう変わるか、implements と extends の使い分け、そしてコンストラクタの引数プロパティが erasableSyntaxOnly や Node.js の型除去実行では使えない点まで、Node.js 22.22.2 での実行結果と一緒に紹介します。エラー文は tsc が出したものをそのまま載せ、ファイル名と行番号の表示だけ省いています。
classの修飾子の早見表
○は読み書きできる、×はコンパイルエラーになる、という意味です。「実行時」は、型チェックを外して JavaScript として動かしたときにどうなるかを表します。
| 修飾子 | クラス内 | サブクラス | 外部 | 実行時 | 違反したときのエラー |
|---|---|---|---|---|---|
public(省略時) |
○ | ○ | ○ | そのまま読み書きできる | なし |
protected |
○ | ○ | × | 読める(型の上だけの制限) | TS2445 |
private |
○ | × | × | 読める(型の上だけの制限) | TS2341 |
#name(#private) |
○ | × | × | 読めない(JavaScript の機能で隠れる) | TS18013 |
readonly |
読む○、代入はコンストラクタ内だけ | 読む○ | 読む○ | 代入できる(型の上だけの制限) | TS2540 |
static |
クラス名.名前 で使う |
クラス名.名前 で使う |
クラス名.名前 で使う |
クラス自体のプロパティになる | インスタンスから読むと TS2576 |
abstract |
宣言だけ書く | 実装が必須 | new できない | 型チェックで消える | new は TS2511、実装漏れは TS2515 |
修飾子は組み合わせて書けます。よく使う形を並べておきます。
class Example {
name: string; // public(省略)
protected price: number; // サブクラスまで
private memo = ""; // このクラスだけ(型の上で)
#stock = 0; // このクラスだけ(実行時も)
readonly id: number; // 作った後は変えない
private readonly code = "A"; // 組み合わせ
static count = 0; // クラスに1つ
static #seq = 0; // クラスに1つ、外から触れない
constructor(id: number, name: string, price: number) {
this.id = id;
this.name = name;
this.price = price;
}
}
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「TypeScript class書き方|private・readonly・継承」
確認した環境とコマンド
| 項目 | 内容 |
|---|---|
| TypeScript | 7.0.2(npm で作業フォルダにインストール) |
| Node.js | 22.22.2 |
| 型チェックのコマンド | npx tsc --noEmit --strict --target es2022 --module nodenext ファイル名.ts |
| JavaScript への変換 | 上のコマンドから --noEmit を外し、--outDir 出力先 を付ける |
classの基本の書き方:プロパティ・コンストラクタ・メソッド
まずは修飾子なしの最小形です。JavaScript との違いは、プロパティをクラスの本体で宣言して型を書く点です。
class Task {
title: string;
done = false; // 初期値から boolean と推論される
constructor(title: string) {
this.title = title;
}
finish(): void {
this.done = true;
}
}
const t = new Task("見積もり");
t.finish();
クラス名はそのまま型としても使えます(const t: Task)。プロパティを宣言しただけで初期値もコンストラクタでの代入もないと、strict では次のエラーになります。
class C { name: string; }
error TS2564: Property 'name' has no initializer and is not definitely assigned in the constructor.
初期値を書く、コンストラクタで代入する、型を string | undefined にする、のどれかで通ります。「後で必ず入る」と決めつけるより、この3つのどれかにしておくほうが読み手にも伝わります。
アクセス修飾子を1つのクラスで試す
早見表の内容を、1つのファイルで確かめます。抽象クラス Item に7種類すべてを書き、サブクラス Book と外部からそれぞれ触ってみます。
abstract class Item {
public name: string;
protected cost: number;
private memo: string;
#secret: string;
readonly id: number;
static count = 0;
private static seq = 0;
constructor(id: number, name: string, cost: number) {
this.id = id;
this.name = name;
this.cost = cost;
this.memo = "m";
this.#secret = "s";
Item.count += 1;
}
abstract label(): string;
inside() {
// クラス内からはすべて読める(エラーなし)
return [this.name, this.cost, this.memo, this.#secret, this.id, Item.seq];
}
rename() { this.id = 2; } // (1) コンストラクタ以外で readonly に代入
}
class Book extends Item {
label() { return `本:${this.name}`; }
sub() {
this.name; // 通る
this.cost; // 通る(protected)
this.memo; // (2)
this.#secret; // (3)
this.id; // 通る(読むだけ)
}
}
const b = new Book(1, "TS入門", 1200);
b.name; // 通る
b.cost; // (4)
b.memo; // (5)
b.#secret; // (6)
b.id = 9; // (7)
b.count; // (8)
Book.count; // 通る(static はサブクラス名からも読める)
new Item(1, "x", 1); // (9)
class Pen extends Item {} // (10)
(1) error TS2540: Cannot assign to 'id' because it is a read-only property.
(2) error TS2341: Property 'memo' is private and only accessible within class 'Item'.
(3) error TS18013: Property '#secret' is not accessible outside class 'Item' because it has a private identifier.
(4) error TS2445: Property 'cost' is protected and only accessible within class 'Item' and its subclasses.
(5) error TS2341: Property 'memo' is private and only accessible within class 'Item'.
(6) error TS18013: Property '#secret' is not accessible outside class 'Item' because it has a private identifier.
(7) error TS2540: Cannot assign to 'id' because it is a read-only property.
(8) error TS2576: Property 'count' does not exist on type 'Book'. Did you mean to access the static member 'Book.count' instead?
(9) error TS2511: Cannot create an instance of an abstract class.
(10) error TS2515: Non-abstract class 'Pen' does not implement inherited abstract member label from class 'Item'.
(番号は対応がわかるように付けたものです。)メッセージは「どのクラスの中でだけ使えるか」まで書いてくれるので、エラーコードと一緒に「within class 〜」の部分を読むと原因がすぐ見つかります。
エラーコードから原因を引く表
| エラーコード | 意味 | よくある場面 | 直し方 |
|---|---|---|---|
| TS2341 | private を外やサブクラスから使った | サブクラスで親の private を読もうとした | サブクラスで使うなら protected にする。外に見せるなら読み取り用のメソッドか get を用意する |
| TS2445 | protected を外から使った | インスタンスから直接読んだ | 公開するメソッドを通す |
| TS18013 | #private をクラスの外から使った | サブクラスや外部から obj.#x と書いた |
get やメソッドを通す |
| TS2540 | readonly に代入した | コンストラクタ以外のメソッドで代入した | 変える必要があるなら readonly を外す。変えないなら新しいインスタンスを作る |
| TS2576 | static をインスタンスから読んだ | obj.count と書いた |
クラス名.count と書く |
| TS2511 | 抽象クラスを new した | 親クラスを直接作ろうとした | 具体的なサブクラスを new する |
| TS2515 | abstract メソッドの実装漏れ | サブクラスで書き忘れた | メッセージに出ているメソッドを実装する |
| TS2564 | プロパティの初期化漏れ | 宣言だけして代入していない | 初期値、コンストラクタでの代入、undefined を含む型のどれかにする |
private と #private の違いを実行時とコンパイル後のJSで比べる
名前が似ている2つですが、private は TypeScript の型チェックだけの約束、#private は JavaScript そのものの機能です。違いは実行して確かめるのがいちばん早いので、型を any にして外から触ってみます。
// run.ts
class Account {
private memo = "memo";
#secret = "secret";
readonly id = 1;
protected cost = 100;
}
const a = new Account();
console.log(Object.keys(a));
console.log(JSON.stringify(a));
console.log((a as any).memo, (a as any)["cost"]);
console.log((a as any).secret);
(a as any).id = 99;
console.log(a.id);
console.log(a["memo"]);
$ node run.ts
[ 'memo', 'id', 'cost' ]
{"memo":"memo","id":1,"cost":100}
memo 100
undefined
99
memo
読み取れることを整理します。
- private と protected は、Object.keys や JSON.stringify の結果にそのまま出ます。 ログや API のレスポンスにオブジェクトを丸ごと渡すと、private のつもりの値も含まれます。
- #secret はどちらにも出ず、外から読む手段がありません。
- readonly も型の上だけの約束で、型を外せば 99 に書き換わりました。
- 最後の行
a["memo"]は、型チェックもエラーなしで通りました。private はブラケット記法で読めるという抜け道が言語仕様として残されています。
コンパイル後のJavaScript
次のクラスを JavaScript に変換して、出力を比べます。
// emit.ts
class Account {
private memo = "memo";
#secret = "secret";
readonly id = 1;
static count = 0;
constructor(public name: string, private age: number) {}
}
target を es2022 にしたときの出力です(クラスの部分を抜き出しています)。
class Account {
name;
age;
memo = "memo";
#secret = "secret";
id = 1;
static count = 0;
constructor(name, age) {
this.name = name;
this.age = age;
}
}
private と readonly の文字は消え、普通のプロパティになっています。#secret はそのまま残ります。 同じファイルを target es2015 で変換すると、#private は WeakMap を使った形に書き換えられました。
var _Account_secret;
class Account {
constructor(name, age) {
this.name = name;
this.age = age;
this.memo = "memo";
_Account_secret.set(this, "secret");
this.id = 1;
}
}
_Account_secret = new WeakMap();
Account.count = 0;
変換先がどちらでも「外から読めない」という性質は保たれます。
private と #private の比較
| 観点 | private name |
#name |
|---|---|---|
| 仕組み | TypeScript の型チェック | JavaScript の言語機能 |
| 実行時に外から読めるか | 読める | 読めない |
| Object.keys・JSON.stringify に出るか | 出る | 出ない |
ブラケット記法 obj["name"] |
型チェックも通る | 書けない |
| 違反時のエラー | TS2341 | TS18013 |
| コンパイル後 | 普通のプロパティ | そのまま(es2015 向けでは WeakMap) |
| コンストラクタの引数プロパティで使えるか | 使える(private x: number) |
使えない(本体で宣言する) |
| protected に相当するもの | あり | なし(サブクラスにも見せない) |
選び方の目安は次のとおりです。値を外に出したくない(ログや JSON に混ぜたくない)ものは #private、サブクラスから使わせたいものは protected、テストコードなどからブラケット記法で覗ける余地を残したい場合や既存コードの書き方に合わせる場合は private、と考えると決めやすくなります。
private があるクラスは、形が同じでも別の型になる
TypeScript の型は基本的に「形が同じなら代入できる」仕組みですが、private なプロパティを持つクラスは例外です。
class P1 { private v = 1; }
class P2 { private v = 1; }
const p: P1 = new P2();
error TS2322: Type 'P2' is not assignable to type 'P1'.
Types have separate declarations of a private property 'v'.
「別々に宣言された private がある」という理由で代入できません。テスト用の差し替えクラスを手書きするときに出会いやすいエラーで、差し替えたい相手は interface で受けるようにすると解決します。次の章の implements がそのための道具です。
readonly と get で「読めるが書けない」を作る
外から読ませたいが書き換えさせたくない、という値には2つの書き方があります。
class G {
#v = 1;
get v() { return this.#v; } // set を書かなければ読み取り専用
}
new G().v = 2;
error TS2540: Cannot assign to 'v' because it is a read-only property.
| 書き方 | クラス内で後から変えられるか | 実行時の保護 | 向いている値 |
|---|---|---|---|
readonly id: number |
変えられない(コンストラクタ内だけ代入できる) | なし(型の上だけ) | ID、作成日時など、一度決めたら変えない値 |
#v と get v() |
メソッドから変えられる | あり | 在庫数、合計など、クラスの中では更新する値 |
static でクラスに1つだけの値を持つ
static を付けたプロパティやメソッドは、インスタンスではなくクラス自体に属します。連番やインスタンスを作る関数(ファクトリ)によく使います。
class Counter {
static #n = 0;
private static m = 0;
protected constructor() {}
static create() { Counter.#n++; return new Counter(); }
}
Counter.m;
new Counter();
error TS2341: Property 'm' is private and only accessible within class 'Counter'.
error TS2674: Constructor of class 'Counter' is protected and only accessible within the class declaration.
static にも private や # を組み合わせられます。また、コンストラクタに protected や private を付けると外から new できなくなり、Counter.create() のような static メソッド経由でだけ作らせる形にできます。作るときに必ず通したい処理(採番や入力の確認)がある場合に便利です。
継承(extends)と super・override の書き方
extends は、親クラスのプロパティとメソッドを引き継いだ新しいクラスを作ります。サブクラスでコンストラクタを書くときは、this を使う前に super(...) を呼ぶのが決まりです。
class A { constructor(public x: number) {} }
class B extends A {
y: number;
constructor() { this.y = 1; }
}
error TS2377: Constructors for derived classes must contain a 'super' call.
error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
constructor() { super(0); this.y = 1; } のように、先に super を呼ぶと通ります。
override でメソッド名の打ち間違いを見つける
親のメソッドを上書きするときに override を付けると、親に同じ名前のメソッドがあるかをコンパイラが確かめてくれます。
class Animal { speak() { return "..."; } }
class Dog extends Animal {
override speek() { return "wan"; } // speak の打ち間違い
}
error TS4117: This member cannot have an 'override' modifier because it is not declared in the base class 'Animal'. Did you mean 'speak'?
正しい名前の候補まで出ます。さらに tsconfig で noImplicitOverride をオンにすると、override を付けずに上書きした箇所も知らせてくれます。
class Cat extends Animal {
speak() { return "nya"; }
}
error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Animal'.
親クラスのメソッド名を変えたときに、上書きしていたつもりのメソッドが取り残されるのを防げます。
abstract で「サブクラスが必ず実装するメソッド」を決める
abstract class は、それ自体は new できず、継承して使うことを前提にしたクラスです。共通の処理は親に書き、種類ごとに違う部分だけを abstract メソッドとして宣言します。早見表のあとで再現したとおり、new すると TS2511、サブクラスで実装を忘れると TS2515 になります。
abstract メソッドには本体を書きません。
abstract class Shape {
abstract area(): number; // 宣言だけ
describe() { return `面積は ${this.area()} です`; } // 共通の処理
}
implements と extends の使い分け
implements は、クラスが interface の形を満たしているかを確かめるための宣言です。extends と違い、何も引き継ぎません。
実装が足りないとき(TS2420)
interface Printable { print(): string }
class Memo implements Printable {
text = "";
}
error TS2420: Class 'Memo' incorrectly implements interface 'Printable'.
Property 'print' is missing in type 'Memo' but required in type 'Printable'.
extends は1つ、implements は複数
interface Priced { price: number }
class Card implements Printable, Priced { // 複数の interface は書ける
price = 100;
print() { return `card ${this.price}`; }
}
class Base { greet() { return "hi"; } }
class Other { bye() { return "bye"; } }
class Multi extends Base, Other {} // クラスは1つだけ
error TS1174: Classes can only extend a single class.
implements は引数の型を教えてくれない
つまずきやすい点が1つあります。implements を書いても、メソッドの引数の型は interface から自動では決まりません。
interface Checker { check(name: string): boolean }
class NameChecker implements Checker {
check(name) { return name.length > 0; }
}
error TS7006: Parameter 'name' implicitly has an 'any' type.
implements は「書き終えたクラスが interface に合っているか」を後から照らし合わせるだけなので、引数の型はクラス側にも書きます(check(name: string))。
比較表
| 観点 | extends |
implements |
abstract class を extends |
|---|---|---|---|
| 相手 | クラス | interface(type で書いたオブジェクト型も可) | 抽象クラス |
| 引き継ぐもの | プロパティとメソッドの実装 | なし(形の確認だけ) | 共通の実装と、実装すべきメソッドの宣言 |
| 指定できる数 | 1つ | 複数 | 1つ |
| 実行時に残るか | 残る(instanceof で判定できる) | 残らない | 残る |
| 向いている場面 | 共通の処理を使い回したい | 差し替え可能にしたい、複数の役割を持たせたい | 共通の処理を持たせつつ、一部を必ず実装させたい |
「処理を引き継ぎたいなら extends、形だけ約束したいなら implements」 が基本です。受け取る側の関数は function show(p: Printable) のように interface で受けておくと、テスト用のクラスにも本番用のクラスにも同じ関数を使えます。
コンストラクタの引数プロパティと、最新環境での注意
コンストラクタの引数に修飾子を付けると、プロパティの宣言と代入を1行で済ませられます。引数プロパティ(パラメータプロパティ)と呼ばれる TypeScript 独自の省略記法です。
// pp.ts
class Member {
constructor(public name: string, private readonly age: number) {}
show() { return `${this.name} (${this.age})`; }
}
console.log(new Member("sato", 30).show());
前の章のコンパイル結果で見たとおり、tsc はこれを「プロパティ宣言」と「this.name = name」に展開します。つまり 型を消すだけでは動く JavaScript にならず、コードの生成が必要な構文です。この性質が、次の2つの環境で影響します。
Node.js で .ts をそのまま実行すると止まる
Node.js 22.22.2 で上のファイルを直接実行した結果です(パスの表示は環境に合わせて置き換えています)。
$ node pp.ts
/home/taro/app/pp.ts:2
constructor(public name: string, private readonly age: number) {}
^^^^^^^^^^^^
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript parameter property is not supported in strip-only mode
code: 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX'
Node.js の公式ドキュメント(TypeScript のページ)には、型除去(type stripping)は v22.18.0 と v23.6.0 から既定で有効、v24.12.0 と v25.2.0 で安定版になったと書かれています。型除去は 型の部分を消すだけの仕組みなので、引数プロパティ、enum、実行時のコードを含む namespace などは対象外とされています。
手元の 22.22.2 では、--experimental-transform-types を付けると同じファイルが動きました。
$ node --experimental-transform-types pp.ts
sato (30)
(node:8137) ExperimentalWarning: Transform Types is an experimental feature and might change at any time
ただし同じ公式ドキュメントに、このフラグは v26.0.0 で取り除かれたと記載があります(v26 での動作は未実行です)。これから書くコードでは、次の「型を消すだけで動く書き方」に寄せておくと、どのバージョンでもそのまま動きます。
erasableSyntaxOnly をオンにすると TS1294
tsconfig の erasableSyntaxOnly は、型を消すだけでは済まない構文をコンパイルエラーにするオプションです。公式の TSConfig リファレンスでは、対象として enum、実行時のコードを含む namespace、import x = require(...)、export =、クラスの引数プロパティ、山かっこ形式の型アサーションが挙げられています。先ほどの pp.ts に付けて型チェックすると、こうなります。
$ npx tsc --noEmit --strict --erasableSyntaxOnly --target es2022 --module nodenext pp.ts
pp.ts(2,15): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
pp.ts(2,36): error TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
型を消すだけで動く書き方に直す
// pp_fixed.ts
class Member {
name: string;
readonly #age: number;
constructor(name: string, age: number) {
this.name = name;
this.#age = age;
}
show() { return `${this.name} (${this.#age})`; }
}
console.log(new Member("sato", 30).show());
$ npx tsc --noEmit --strict --erasableSyntaxOnly --target es2022 --module nodenext pp_fixed.ts
(エラーなし)
$ node pp_fixed.ts
sato (30)
行数は増えますが、プロパティの一覧がクラスの先頭にまとまるので、どんなデータを持つクラスかが読み取りやすくなります。
| 実行のしかた | 引数プロパティ | 対応 |
|---|---|---|
| tsc で JavaScript に変換してから実行 | 使える | そのままでよい |
| tsc で erasableSyntaxOnly をオン | TS1294 | プロパティ宣言と代入に書き分ける |
| Node.js で .ts を直接実行 | ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX | 同上 |
public・private・protected・readonly・abstract・implements・override は、プロパティやメソッドに付けるぶんには型を消すだけで済むので、どの実行方法でも使えます。pp_fixed.ts が readonly と #private を使ったまま直接実行できたことが、その確認になっています。
コピペ用:修飾子をひととおり使った完成形
ここまでの要素をまとめた例です。備品を表す抽象クラスと、2つのサブクラスを定義しています。
// full.ts
interface Printable {
print(): string;
}
abstract class Item implements Printable {
static #nextId = 1; // クラス全体で共有する連番(外から触れない)
readonly id: number; // 作った後は変えない
#stock = 0; // 実行時にも外から読めない
constructor(
readonly name: string, // 引数プロパティ(tsc で変換して実行する前提)
protected price: number, // サブクラスから使う
) {
this.id = Item.#nextId++;
}
get stock() { return this.#stock; }
add(n: number) {
if (n <= 0) throw new RangeError("1以上を指定してください");
this.#stock += n;
return this;
}
abstract category(): string; // サブクラスに実装を任せる
print() {
return `#${this.id} [${this.category()}] ${this.name} ${this.price}円 在庫${this.#stock}`;
}
}
class Book extends Item {
constructor(name: string, price: number, private pages: number) {
super(name, price);
}
override category() { return "書籍"; }
override print() { return `${super.print()} ${this.pages}ページ`; }
}
class Pen extends Item {
override category() { return "文具"; }
discount(rate: number) { this.price = Math.round(this.price * (1 - rate)); return this; }
}
const items: Printable[] = [
new Book("TS入門", 2400, 320).add(3),
new Pen("ボールペン", 150).add(10).discount(0.2),
];
for (const i of items) console.log(i.print());
console.log(items[0] instanceof Item, items[0] instanceof Book, items[1] instanceof Book);
$ npx tsc --strict --noImplicitOverride --target es2022 --module nodenext --outDir out full.ts
$ node out/full.js
#1 [書籍] TS入門 2400円 在庫3 320ページ
#2 [文具] ボールペン 120円 在庫10
true true false
この例は引数プロパティを使っているので、tsc で変換してから実行しています。node full.ts と直接実行した場合は、前の章と同じ ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX になることも確かめました。直接実行したいときは、pp_fixed.ts と同じ要領でプロパティ宣言と代入に書き分けてください。
修飾子を選ぶチェックリスト
| 確認すること | 選ぶもの |
|---|---|
| 外から読み書きしてよいか | よい → 修飾子なし(public) |
| サブクラスからは使わせたいか | 使わせたい → protected |
| ログや JSON にも出したくないか | 出したくない → #name |
| 型の上で隠せれば十分か | 十分 → private |
| 作った後に変わらないか | 変わらない → readonly |
| インスタンスごとではなくクラスに1つでよいか | よい → static |
| サブクラスごとに中身が違うメソッドか | 違う → abstract |
| 親のメソッドを上書きしているか | している → override を付ける |
| .ts を直接実行する環境か | そうなら引数プロパティは使わず、本体で宣言する |
よくある質問
Q. private と #private はどちらを使えばよいですか? A. 実行時にも外から読めないようにしたい値、Object.keys や JSON.stringify の結果に混ぜたくない値は #private が合います。サブクラスから使わせたい値は protected、型の上で隠せれば十分な値や既存のコードが private でそろっている場合は private、と使い分けるのが目安です。1つのクラスの中で混ぜて使っても問題ありません。
Q. readonly を付けたプロパティは本当に書き換えられませんか? A. コンパイル時には TS2540 で止めてくれますが、実行時の保護はありません。本文の実行結果のとおり、型を any にすれば代入できました。実行時にも守りたい場合は、#private のフィールドと get の組み合わせにします。
Q. interface と abstract class はどう使い分けますか? A. 共通の処理(実装)を持たせたいなら abstract class、形だけを約束したいなら interface です。interface は複数を同時に implements でき、実行時には何も残りません。abstract class は1つしか extends できませんが、instanceof で判定できます。
Q. コンストラクタの引数プロパティはもう使わないほうがよいですか? A. tsc や各種ビルドツールで JavaScript に変換してから動かす構成なら、これまでどおり使えます。Node.js で .ts を直接実行する場合や erasableSyntaxOnly をオンにする場合は使えないので、その環境ではプロパティをクラスの本体で宣言する書き方にそろえます。
まとめ
- public・protected・private の違いは「どこから使えるか」。違反すると protected は TS2445、private は TS2341 になる
- private と readonly は型の上だけの約束で、コンパイル後は普通のプロパティになり、Object.keys や JSON.stringify にも出る
- #private は JavaScript の機能で、実行時にも外から読めない。違反は TS18013
- static は
クラス名.名前で使う。インスタンスから読むと TS2576 - abstract class は new できず(TS2511)、abstract メソッドの実装漏れは TS2515 で見つかる
- extends は処理を引き継ぐ(1つだけ)、implements は形を確かめる(複数可)。implements しても引数の型はクラス側に書く
- 引数プロパティは、Node.js の型除去実行と erasableSyntaxOnly では使えない。プロパティ宣言と代入に書き分ければどの環境でも動く
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「TypeScript class書き方|private・readonly・継承」
株式会社RJCでは、TypeScriptのclassの書き方やアクセス修飾子の使い分けを仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。