はじめに
Udemyの【世界で7万人が受講】Understanding TypeScript 日本語版を参考にTypeScriptを学習したので、プログラミングノート(カンペ用ノート)をします。
(一部、JavaScriptでもそのまま使えるものもあります。)
クラス・コンストラクタ・this・private
class Department {
name: string;
private employees: string[] = []; // privateはクラス内部からのみアクセス可能。内部のメソッド等からアクセスすること強制する。
// コンストラクタはインスタンス生成時に実行される。
constructor(n: string) {
this.name = n;
}
describe(this: Department) {
// thisにより、常にDepartmentクラスが参照されるよう強制できる。
console.log("Department:" + this.name); // クラスのプロパティにクラスの中からアクセスするにはthisを使う。使わないと、windowオブジェクトが指定されたりする。
}
//従業員を追加するメソッド
addEmployees(employee: string) {
this.employees.push(employee);
}
// 従業員を出力するメソッド
printEmployeeInformation() {
console.log(this.employees.length);
console.log(this.employees);
}
}
const accounting = new Department("Accounting");
console.log(accounting);
accounting.addEmployees("Max");
accounting.addEmployees("Manu");
// accounting.employees("Anna"); //employeesはprivateなのでアクセス不可。
accounting.describe();
accounting.printEmployeeInformation();
プロパティ初期化のショートカット構文・readonly
修正前
private readonly id; //readonlyにより以降更新不可。
name: string;
private employees: string[] = [];
constructor(n: string) {
this.name = n;
}
修正後
private employees: string[] = [];
constructor(private readonly id: string, public name: string) {} // readonlyにより、以降更新不可。
継承
// 継承 プロパティやメソッドを全て受け継ぐ。何も記載がなければ、コンストラクタも自動で呼び出される。
class ITDepartment extends Department {
admins: string[];
constructor(id: string, admins: string[]) {
super(id, "IT"); // thisを使うためには先にsuperを呼ぶ必要がある
this.admins = admins;
}
}
// const accounting = new Department("Accounting");
const it = new ITDepartment("d1", ["MAX"]);
console.log(it); // ベースクラスで定義されたプロパティも出力される。
オーバーライド
class Department {
protected employees: string[] = []; // protectedはサブクラスからもアクセス可能
// 従業員を追加するメソッド
addEmployees(employee: string) {
this.employees.push(employee);
}
}
// サブクラス
class AccountingDepartment extends Department {
// オーバーライド
addEmployees(name: string) {
if (name === "Max") {
return;
}
this.employees.push(name); 親クラスのemployeesにアクセス可能
}
}
GetterとSetter
特別なロジックをプロパティの中にカプセル化して、そのプロパティを設定したり取り出したりすることを可能とする。
class AccountingDepartment extends Department {
private lastReport: string;
// Getter 値を取得するメソッド。外部からprivateの値出力が可能。プロパティとしてアクセス可能。
get mostRecentReport() {
if (this.lastReport) {
return this.lastReport;
}
throw new Error("レポートが見つかりません");
}
// Setter
set mostRecentReport(value: string) {
if (!value) {
throw new Error("正しい値を設定してください。");
}
this.addReport(value);
}
}
const accounting = new AccountingDepartment("d2", []);
accounting.mostRecentReport = "通期会計レポート"; // Setter。メソッドと違って()は不要。
console.log(accounting.mostRecentReport); // Getter。メソッドと違って()は不要。
static
オブジェクトを作らなくてもクラスのメソッドにアクセスできるようにするもの。
staticでない部分からthisを使ってもアクセスできない。(thisは生成されたインスタンスを指すから。)
class Department {
// staticプロパティ
static fiscalYear = 2020;
// staticメソッド
static createEmployee(name: string) {
return { name: name }; //nameというキーにnameという値を設定したオブジェクトを返す
}
}
const employee1 = Department.createEmployee("Max"); //Departmentクラスのインスタンスは作成不要。
console.log(employee1, Department.fiscalYear);
abstruct
具体的な実装はサブクラスで提供することを強制する。
abstructクラス自身はインスタンス化できず、必ず継承して使う。
abstract class Department {
constructor(protected readonly id: string, public name: string) {}
abstract describe(this: Department): void; // 親クラスでメソッドの実装はできない
}
class AccountingDepartment extends Department {
constructor(id: string, private reports: string[]) {
super(id, "Acconting");
}
// describeメソッドを提供することが強制される。
describe() {
console.log("会計部門 - ID:" + this.id);
}
}
accounting.describe();
シングルトン
クラスのインスタンスを一つしか作らせないデザインパターン
class AccountingDepartment extends Department {
private lastReport: string;
private static instance: AccountingDepartment; // staticなので、クラスの持っているフィールド。privateなのでクラスの中からのみアクセス可能。
//コンストラクタがprivateだから、newしたインスタンスからはアクセス不可。クラスの内部からのみアクセス可能。
private constructor(id: string, private reports: string[]) {
super(id, "Acconting");
this.lastReport = reports[0];
}
static getInstance() {
// staticメソッドなので、クラスのメソッド
if (this.instance) {
// thisはクラス自体を指す
return this.instance;
}
// 以下のコードは一度しか実行されない。二回目はif文の中が実行される、
this.instance = new AccountingDepartment("d2", []);
return this.instance;
}
}
const accounting = AccountingDepartment.getInstance(); // newできない。
インターフェース
クラスでのinterface
オブジェクトの構造を定義
interface Named {
readonly name: string; // 初期化子は使用できない。構造のみを定義
// interfaceにreadonlyをつけると、implementsしたクラスのnameプロパティもreadonlyになる。
}
// 指定した構造は作成必須。abstructは具体的な実装をもてる。interfaceは抽象化された実装しか持てない。
interface Greetable extends Named {
// interfaceはinterfaceを継承可能。
// カスタム型 type Person = {...}でも同様に扱える。interfaceの方が限定的であることを伝えられる。
greet(phrase: string): void;
}
// 継承は一つのクラスしか継承できないが、implementsは複数interfaceを実装できる。
class Person implements Greetable { // imprementsしたクラスはgreetable(挨拶可能)であることを明記
name: string;
age = 30;
constructor(n: string) {
this.name = n;
}
greet(phrase: string) {
console.log(phrase + " " + this.name);
}
}
let user1: Greetable; // interfaceを型として使える。
user1 = new Person("Max");
user1.greet("Hello I am");
console.log(user1);
?をつけたプロパティは実装の要否が任意
interface Named {
readonly name?: string;
}
関数のinterface
typeの代わりにinterfaceで関数の構造を定義可能。
以下の二つは同じ意味。
type AddFn = (a: number, b: number) => number;
interface AddFn {
(a: number, b: number): number; // 匿名メソッド
}