TypeScriptとは
Javascriptにコンパイルされる静的型システムがついたJavascriptの上位集合。
Javascriptにコンパイル
TypeScriptPlaygroundで試すとわかるが、左側のエディタでtypescriptを書くと、
const hello: string = 'hello'
右側のエディタにjsにコンパイルされたものが表示される
const hello = 'hello';
静的型システム
TypescriptがJavascriptにコンパイルされる際に型検査を行うシステム。
Javascriptの上位集合
現行のJavaScriptで記述されたプログラムはすべて、TypeScriptのプログラムとして実行が可能。
はじめに
NodejsでTypescriptインストール
npm install -g typescript
とターミナルに入力しインストール。
権限エラーが表示された場合、
sudo npm install -g typescript
Javascriptにコンパイル
Typescriptをインストールできたらtsc
というコマンドが使用できるようになり、tsc Typescriptのファイル名
と入力すると、コンパイルされたjsファイルが自動生成される。
例)
ts拡張子のtypescriptファイルを作成しts記述
const hello: string = "hello";
console.log(hello);
ターミナルでtsc index.ts
と入力するとコンパイルされjsファイルが生成。
var hello = "hello";
console.log(hello);
Typescriptを使うべき理由
- 型を定義していく仕様上、半自動的にドキュメントのようなコードが書ける。
- コンパイル時にエラー表示してくれる型付きLinterとしての側面がある。
- ES5へのコンパイラ
デフォルトだとES3にコンパイルされる。
tsc index.ts --target ES6
などと、コンパイルターゲットも指定できる。
Typescriptの型はこう書く!
一般的には、変数、関数の仮引数、関数の戻り値など値に対して型をつける。
boolean型、number型、string型
下記のように記述することでそれぞれの型を宣言できる。
const hasValue: boolean = true;
const count: number = 10; //整数も浮動小数点もマイナスも同じ
const single: string = "hello";
型注釈と型推論
下記のように: boolean
などの型を記述する書き方を型注釈という。
const hasValue: boolean = true;
この型注釈を省力すると右の値から推測をして自動的に型を決めてくれる。
それを型推論という。
let value = 3.333;
vscodeではTypescriptが内蔵されていて、変数をhoverすると型を確認できる。
下記のように型推論によって、value
はnumber型になっている。
型が異なる値を代入しようとするとエラー表示してくれる。
オブジェクトに型をつける
普通に書くと型推論によって、プロパティごとに型を決めてくれる。
const person = {
name: "jack",
age: 26,
};
型注釈で書いた場合
const person : {
name: string; // セミコロンであることに注意
age: number;
} = {
name: "jack",
age: 26,
};
配列に型をつけるArray型
型推論で書く
const fruits = ["apple", "banana", "grape"];
型注釈で書く
const fruits: string[] = ["apple", "banana", "grape"];
異なる型を配列に格納したい場合
型推論で書くと、ユニオン型になり、二つの型を代入できるようになる。
ユニオン型の型注釈は、2つ以上の型をパイプ記号(|)で繋げて書きます。
サバイバルTypescript:ユニオン
const fruits = ["apple", "banana", "grape", 1];
型注釈の場合、上記ユニオン型以外にany型でも書ける。
TypeScriptのany型は、どんな型でも代入を許す型です。プリミティブ型であれオブジェクトであれ何を代入してもエラーになりません。
const fruits: any[] = ["apple", "banana", 1];
Tuple型
決まった内容の配列を作るときなどに使用するタプル型。
配列の強化版のようなイメージ。
タプル型は、複数の値を保持することのできる型です。 [] 記号を使うところも配列によく似ていますが、それぞれの位置の要素の型を明示しておくことができます。
タプル型を型注釈で書く。
const books: [string, number, boolean] = ['business', 1500, false]
異なる型を代入しようとしたり、指定した要素数以上に追加したりするとエラー表示。
※pushで追加した場合、エラー表示されない...その追加した要素に参照しようとするとエラーが出るが、途中経過までは見ないらしい🤔
Enum型
特定のまとまったグループのみを受け入れる型。
Enum(列挙)型とは名前付きの定数を定義するためのもの。
定義にはenumを使用し、定数名はすべて大文字とする。
例えば、coffee.size
には特定の4つの文字列しか受け入れたく無い場合。
enum CoffeeSize {
SHORT = 'SHORT',
TALL = 'TALL',
GRANDE = 'GRANDE',
VENTI = 'VENTI'
}
const coffee = {
hot: true,
size: CoffeeSize.TALL
}
coffee.sizeの型を確認するとCoffeeSizeの列挙型になっていることがわかり、この列挙型とは異なる型を受け入れなくなる。
従って、代入する場合は下記のように、CoffeeSize列挙型からしかできない。
coffee.size = CoffeeSize.SHORT
any型
すでに挙げている通り、なんでも許容する型。
TypeScriptのany型は、どんな型でも代入を許す型です。プリミティブ型であれオブジェクトであれ何を代入してもエラーになりません。
jsの世界に戻るようなものなので、正しくtsの型の恩恵を受けたいなら基本的にany型は使わない。
any型、型注釈
let anything: any = true;
どんな値を代入してもエラーにはならない
anything = 'hello';
anything = [];
anything = {};
anything.aaaa = 'aaaa';
型定義してあったものにも代入できてしまう..なので基本any型は使用しない🤨
let sample = 'sample'; // 型推論により、string型
sample = anything
Union型
複数の型を受け入れる型。or演算子のように|
で受け入れたい型を繋げて記述する。
let unionType: number | string = 10;
unionType = 'hello';
unionType = 1
配列のUnion型の書き方。
下記のように書けば、number型、string型のみ受け入れる配列になる。
let unionTypes: (number | string)[] = [ 20, 'hello' ]
Literal型
特定の決まった値のみを扱う型。
下記の場合、appleのリテラル型となり、apple以外は入れられなくなる。
let apple: 'apple' = 'apple'
数値の場合も同様に、下記の変数number
は1以外を代入できない
let number: 1 = 1
number = 0 // エラー
型推論で書く場合、const
を使用した時点でリテラル型になる。
const apple = 'apple' // 型推論により、リテラル型になる
特定の値しか受け入れたく無い場合、このliteral型とunion型を組み合わせることで、enum型と似たようなやり方もできる。
下記の場合だと3つの文字列しか受け付けない変数になる。
let clothSize: 'small' | 'medium' | 'large' = 'small'
enum型との使い分けとすると、基本的にenum型の方が扱いやすい場合が多いので、enum型を使用し、逆に受け入れる値が少ない場合などは、literal型とunion型を組み合わせたやり方でもいいかもしれない。
typeエイリアスとは
型を変数のように扱える記述の仕方。
type 変数名 = 値
という記述。letやconstと似ている。
type ClothSize = "small" | "medium" | "large"
let clothSize: ClothSize = "small";
関数に型を適応させる方法
関数は仮引数と、戻り値に型を適応させる。
戻り値の型は()の後に記述する。
戻り値に応じた型推論もできるが基本的に戻り値の型も型注釈したほうがいい。
仮引数の型は型推論できないので、必ず型注釈する。
// 関数宣言
function add(num1: number, num2: number): number {
return num1 + num2;
}
// アロー関数1
const doubleNumber = (num1: number): number => num1 * 2
// アロー関数2(関数を格納している変数に型注釈した記述)
const doubleNumber: (num1: number) => number = num1 => num1 * 2
void型
callback関数に型を適応
cb: (num: number) => number
部分でコールバック関数の型注釈。
function doubleAndHandle(num: number, cb: (num: number) => number): void {
const doubleNum = cb(num * 2);
console.log(doubleNum);
}
doubleAndHandle(20, (doubleNum) => {
return doubleNum;
});
unknown型を使って、柔軟でany型より厳しい型を定義する方法
unknown型はany型のように、どんな型の値も代入できるが、any型と違って、
どんな値にも入れられるわけではない。
下記の例では
text = unknownInput;
部分でエラーが出ていることがわかる。
never型
決して何も返さない型
例えばnew Error()
ではundefinedも何も返さないので、never型を使用する
function error(message: string): never {
throw new Error(message);
}
console.log(error("This is an error"));
コンパイラを使う方法
watchモードで自動コンパイル
ターミナルで下記入力するとwatchモードになり、保存時に自動コンパイルされる。
tsc index.ts -w
ファイルを一気にコンパイル
スペースで続けてファイル名を指定すると複数のコンパイルができる。
tsc index.ts text.ts
プロジェクトルートにtsconfig.jsonを設置することでtsc
と入力するとtsconfig.jsonに設定されている内容に沿って、処理される。
デフォルトでは全てのtsファイルがコンパイルされる。
tsc --init
→ tsconfig.jsonが生成
tsc
→ 全tsファイルコンパイル
tsc -w
→ watchモード
includeとexcludeを使ってコンパイルファイルを指定
tsconfig.jsonのcompilerOptions
プロパティの後などにincludeやexcludeプロパティを記載して、コンパイル対象を指定できる。
ワイルドカードが使用できる。
"include": []
→ コンパイルするファイルの指定。
"exclude": []
→ コンパイルしないファイルの指定。
node_modules
はデフォルトではexcludeされているが、excludeを記述した場合は、node_modules
も明記しないとコンパイルされてしまうので、注意。
{
"compilerOptions": {},
"exclude": [
"test.ts",
"*.spec.ts",
"tmp/compiler.ts"
"**/compiler.ts"
"node_modules"
]
}
files
プロパティもあり、include
と似ているが、こちらはワイルドカードが使用できないし、exclude
より優先されるという違いがある。
デフォルトではinclude
もfiles
も記述されていないので、全てのtsファイルがコンパイルされる。
tsconfig.jsonのcompilerOptionsプロパティの解説
オプション | 説明 |
---|---|
target | コンパイルターゲットの指定。es5 などと記述する |
lib | tsが用意する型の定義記述。指定されない場合はtargetに応じて自動的に判断されるので、基本的にこちらは記述しなくてok |
allowJs | js拡張子をコンパイル対象にするかどうか。outDirなどと組み合わせて、コンパイルしたものを別のフォルダに格納したりする必要がある。 |
checkJs | 単体ではtrueにできず、allowJsと一緒に使う。jsファイルもエラーチェックするかどうか。 |
jsx | reactjsの時に使用。 |
declaration | これをtrueにすると、コンパイルしたtsファイルの中でexportしているもの全ての型定義ファイルをファイルごとに作成する。 |
declarationMap | declarationオプションと併用するオプション。これをtrueにすると、型定義のmapファイルが作成される。 |
sourceMap | trueにして、コンパイルするとmapファイルが生成される。ブラウザでコードを見た際にtsファイルが見れるようになる。 |
outDir | tsがコンパイルされた際に生成するjsファイルの出力先を指定できる。"outDir": "./dist" のように記述するとdistディレクトリが生成され、その配下にコンパイルされたjsが生成される。 |
rootDir | コンパイル結果をoutDirで出力する際に、どのディレクトリ配下のディレクトリ構造で出力するかを指定する。 |
romoveComments | コメントを消すかどうか。trueにすると消える。 |
noEmit | 何も出力しない。tsのチェックだけを行なって何も出力しない。 |
downlevelIteration | targetがes3とes5の時のみ使用できる。反復処理の際に正しく動作しなかったらtrueにしてみたりする。 |
noEmitOnError | trueにするとtsエラーが起こった際にjsファイルを生成しないようになる。 |
strict | このオプション自体は特定の機能を有効にするものではなく、このオプションをtrueにすると、下記のnoImplicitAny ~ alwaysStrict の7つのオプションが全てtrueになる。 |
noImplicitAny | 暗黙的にanyになる値をエラーにする。strict をtrue にした上で、任意のルールを一つずつfalse にすることが可能。 |
strictNullChecks | Nullableな値に対してオプションの呼び出しを行う記述をエラーにする。 |
strictFunctionTypes | 関数代入時の引数の型チェックにおいて、TypeScriptのデフォルトはBivariantlyな挙動だが、このオプションをtrueにするとContravariantlyに型チェックが走るようになる。 |
strictBindCallApply | bind, call, applyを使用する際に、より厳密に型チェックが行われるようになる。 |
strictPropertyInitialization | クラスを使用するときに使うもの。 |
noImplicitThis | 使われているthisの型が暗黙的にanyになる場合にエラーにする。 |
alwaysStrict |
"use strict"; を必ず全てのファイルの先頭行に付与する。 |
コードの品質を保つ設定
オプション | 説明 |
---|---|
noUnusedLocals | 宣言されたが使用されていない変数が存在する場合にコンパイルエラーにする。デフォルト値はfalse。とりあえずtrueにしておく。 |
noUnusedParameters | 関数の作成時、定義しているのに中身のコードで使用されない場合にコンパイルエラーにする。これもとりあえずtrueにしておいて良さそう。 |
noImplicitReturns | 暗黙的なreturnをエラーにするかどうか。関数内で、条件分岐の条件によって明示的なreturnがされないルートがある場合、コンパイルエラーになる。 |
noFallthroughCasesInSwitch | switch文を使用。fallthroughなcaseのうち、1行以上処理が存在しているにも関わらず脱出処理(breakやreturn)が無いものにエラーを吐く。trueにしておくと良さそう。 |
TypescriptではClassをこう使う!
オブジェクト指向プログラミング(OOP)とは
- OOPとは現実世界の物に見立ててプログラムする方法
- どのようにアプリケーションを作るかという方法の一つ
- 人間にとってわかりやすくロジックを分割する方法の一つ
オブジェクトを作る方法はオブジェクトリテラルとClassの二つある
オブジェクトリテラル
{
name: {
firstName: 'Peter',
lastName: 'Quill'
},
age: 36,
gender: 'male'
}
Class
- オブジェクトの設計図
- Claddから作られたオブジェクトはインスタンスと呼ばれる
- 似たようなオブジェクトを複数作成する時に便利
Classを定義してオブジェクト作成する方法
class Person {
name: string; // フィールド
constructor(initName: string) { // 初期処理
this.name = initName;
}
}
const quill = new Person("Quill");
console.log(quill); // → Person { name: 'Quill' }
Classにメソッドを追加する方法
class Person {
name: string;
constructor(initName: string) {
this.name = initName;
}
// メソッド追加
greeting() {
console.log(`Hello! My name is ${this.name}`);
}
}
const quill = new Person("Quill");
quill.greeting(); // → Hello! My name is Quill
thisの使用には注意。
Tsは直接的な記述だったら面倒を見てくれるが間接的なものまでは見てくれない。
上記のquillインスタンスがある状態で、下記サンプル記述して確認。
const anotherQuill = {
anotherGreeting1: quill.greeting, // nameはないのにエラー表示されない
anotherGreeting2() {
console.log(`Hello! My name is ${this.name}`); // 直接的に記述した場合はエラー表示してくれる
}
};
anotherQuill.anotherGreeting1(); // → Hello! My name is undefined
anotherGreeting1
の型情報を確認するとvoidのみ。
Tsにメソッド内でthisを使用しているかどうか伝える方法
メソッドの第一引数にthis: {プロパティ: 型}
を追記すると型情報が追加される。
calss Person
のgreetingメソッドに追記
greeting(this: { name: string }) {
console.log(`Hello! My name is ${this.name}`);
}
anotherGreeting1
の型情報が追加されて、anotherQuill.anotherGreeting1();
のメソッド実行部分でもエラー表示されるようになったことがわかる。
このようにすることで間接的な状態でもメソッドの中でthisが使用されているかどうかと、そのthisの情報を確認、エラー表示してくれるようになる。
Classは型として使用できる
TsではClassを作った場合、そのClassが作り出すインスタンスを表す型も同時に作っている。
greeting(this: { name: string })
部分にPerson型を指定して、より厳格で安全なコードになる。
greeting(this: Person) {
console.log(`Hello! My name is ${this.name}`);
}
public修飾子とprivate修飾子でアクセス制限
classのフィールドやメソッドにはpublic修飾子とprivate修飾子というものが使用できる。
接頭辞としてつけることで使用でき、アクセス制限をかけられる。
接頭辞を何もつけないとデフォルトでは、public修飾子になり、どこからでもアクセス可能。
例)
class Person
にageフィールドを追加。
class Person {
name: string;
age: number; // → 追加。接頭辞をつけていないので、public修飾子。
constructor(initName: string, initAge: number) { // → 追加
this.name = initName;
this.age = initAge; // → 追加
}
greeting(this: Person) {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
}
}
const quill = new Person("Quill", 38);
quill.age = 50; // → classの外側からアクセスでき、変更可能。
quill.age = 50;
のようにclassの外側からいくらでもアクセスできる。
private修飾子をフィールド名につけることで、class内でしかアクセスできなくなる。
メソッドも同様にprivate修飾子をつけることで外側で使用できなくなる。
class Person {
name: string;
private age: number; // → private修飾子追加
constructor(initName: string, initAge: number) {
this.name = initName;
this.age = initAge;
}
greeting(this: Person) {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
}
}
const quill = new Person("Quill", 38);
quill.age = 50; // → エラー表示される。
初期化処理の省略
constructor()のパラメーターで、修飾子 フィールド名: 型
と記述することで
classのフィールド名やconstructor()
の処理を省略できる。
// 省略していない場合
class Person {
name: string;
constructor(name: string) {
this.name = initName;
}
}
// 省略した場合
class Person {
constructor(public name: string) {
}
}
readonly修飾子で書き換え不可にする
public修飾子、private修飾子の他にreadonly修飾子というものがある。
読み込み専門になる修飾子。
下記の例ではprivate age
にreadonly
修飾子も追加。
メソッドで書き換えようとするがエラー表示。
readonly
修飾子を外せば正常に動作する。
class Person {
constructor(public name: string, private readonly age: number) {}
// メソッド追加
incrementAge() {
this.age = +1; // ageはreadonly修飾子がついているので、エラー
}
greeting(this: Person) {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
}
}
const quill = new Person("Quill", 38);
quill.greeting();
extendsを使用して他のclassの機能を継承
class 新しいcalss名 extends 継承元class名 {}
とすることで、継承元のcalssの機能を継承している新しいclassを生成できる。
class Person {
constructor(public name: string, private age: number) {}
incrementAge() {
this.age = +1;
}
greeting(this: Person) {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
}
}
class Teacher extends Person {} // extendsで継承したTeacher class
const quill = new Person("Quill", 38);
quill.greeting(); // → Hello! My name is Quill. I am 38 years old.
const tom = new Teacher("Tom", 28);
tom.greeting(); // → Hello! My name is Tom. I am 28 years old.
constructor関数を拡張したい場合
派生クラスのconstructor関数拡張にはsuper関数
の呼び出しを行う必要がある。
下記の例では派生クラスTeacherに新しいプロパティとして、subjectを追加している。
class Teacher extends Person {
constructor(name: string, age: number, public subject: string) {
super(name, age);
}
}
継承元のメソッドも上書き、変更したい場合は単純に新しいclassで、そのメソッドと処理を記述する。
下記の例ではgreething()を上書き。
class Teacher extends Person {
constructor(name: string, age: number, public subject: string) {
super(name, age);
}
greeting() {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old. I teach ${this.subject}`);
}
}
そのままだとエラーが発生!ageは継承元のclass Person
内で、private修飾子
がついているため、そのclassの外ではアクセス不可。
継承先では使いたいけど、publicで、どこからでもアクセスはしてほしくはない。
そんな場合にprotected修飾子を使う。
protected修飾子を使用し、継承先ではアクセスできるようにする
protected修飾子でクラスとその派生クラスではアクセスできるプロパティになる。
class Person {
// ageの修飾子をprotectedに変更
constructor(public name: string, protected age: number) {}
incrementAge() {
this.age = +1;
}
greeting(this: Person) {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
}
}
class Teacher extends Person {
constructor(name: string, age: number, public subject: string) {
super(name, age);
}
greeting() {
// 派生クラスでもthis.ageにアクセスできるようになる。
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old. I teach ${this.subject}`);
}
}
const quill = new Person("Quill", 38);
quill.greeting();
const tom = new Teacher("Tom", 28, "Math");
tom.age = 30 // クラスや派生クラスの外ではアクセス不可でこちらはエラーになる。
tom.greeting();
getterとsetterを作る
getter
何か取得した時に処理を発火したい場合に使用
setter
何か代入した時に処理を発火したい場合に使用
例)
subjectの値を取得(get)して空文字だったらエラー処理したい。
class Teacher extends Person {
// get追加
get subject() {
if (!this._subject) {
throw Error("There is no subject.");
}
return this._subject;
}
constructor(name: string, age: number, private _subject: string) { // private _subjectに変更
super(name, age);
}
greeting() {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old. I teach ${this.subject}`);
}
}
const tom = new Teacher("Tom", 28, "");
console.log(tom.subject); // → Error!空文字じゃなかったらthis._subjectの値が返ってくる。
例)
subjectの値を代入(set)して空文字だったらエラー処理したい。
class Teacher extends Person {
get subject() {
if (!this._subject) {
throw Error("There is no subject.");
}
return this._subject;
}
// set追加。getterと同じ名前でも可。
set subject(value) {
if (!this._subject) {
throw Error("There is no subject.");
}
this._subject = value;
}
constructor(name: string, age: number, private _subject: string) {
super(name, age);
}
greeting() {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old. I teach ${this.subject}`);
}
}
const tom = new Teacher("Tom", 28, "Math");
tom.greeting();
tom.subject = ""; // setterはこの代入した時に発火
console.log(tom.subject); // → 空文字なのでError!
staticを使用してインスタンスを生成せず、Classを使用
static
プロパティ/メソッドを使うと、newしてクラスのインスタンスを作らずとも、クラスのプロパティ、メソッドを使える。
// staticメソッドの例
Math.random()
例)
Person Classにstaticプロパティ、メソッド追加。
const quill = new Person("Quill", 38);
などのインスタンス生成は削除。
class Person {
static species = "Homo sapiens"; // staticプロパティ追加
static isAdult(age: number) { // staticメソッド追加
if (age > 17) return true;
return false;
}
constructor(public name: string, protected age: number) {}
incrementAge() {
this.age = +1;
}
greeting(this: Person) {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
}
}
class Teacher extends Person {
// 省略
}
// インスタンスは生成していないのにstaticプロパティ、メソッドにはアクセス可能。
console.log(Person.species); // → Homo sapiens
console.log(Person.isAdult(14)); // → false
// 派生クラスも同様。
console.log(Teacher.species); // → Homo sapiens
console.log(Teacher.isAdult(30)); // → true
static
プロパティ/メソッドはインスタンスを生成しないためthisを使用できない。
thisはインスタンスを指すため。
thisは使用せず直接、クラス名.プロパティ名
のようにアクセスする。
例)Person.species
abstractクラスを使用して継承にのみ使用できるクラスを作成
abstract = 抽象的な
abstractが指定され、インスタンス化できない、継承元としてのみ使用するクラスを抽象クラスという。
abstractは抽象クラスを作成する時に宣言します。抽象クラスとは直接インスタンス化(new)することができず、必ずスーパークラスとして利用することを保証するものです。
クラスの前にabstract修飾子
をつけることでそのクラスは抽象クラスだと明示でき、abstractメソッド
やabstractプロパティ
を使用できる。
つまりabstractクラスでしかabstractメソッド、プロパティは使用できない。
例)
Personクラスを抽象クラス化して、greeting()
の中で派生クラスによって処理が異なるメソッドを実行する。
// Personクラスにabstract修飾子を追加して抽象クラス化
abstract class Person {
static species = "Homo sapiens";
static isAdult(age: number) {
if (age > 17) return true;
return false;
}
constructor(public name: string, protected age: number) {}
incrementAge() {
this.age = +1;
}
greeting(this: Person) {
console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
// 派生クラスによって処理が異なるメソッドexplainJobを実行。
this.explainJob();
}
// abstractメソッドを定義。これによって派生クラスで、explainJob()があることを保証する。
abstract explainJob(): void;
}
class Teacher extends Person {
// abstractメソッド、プロパティは必ず派生クラスで記述しなければならない。
explainJob() {
console.log(`I am a teacher and I teach ${this.subject}.`);
}
get subject() {
if (!this._subject) {
throw Error("There is no subject.");
}
return this._subject;
}
set subject(value) {
if (!this._subject) {
throw Error("There is no subject.");
}
this._subject = value;
}
constructor(name: string, age: number, private _subject: string) {
super(name, age);
}
}
const tom = new Teacher("Tom", 28, "Math");
tom.greeting(); // → Hello! My name is Tom. I am 28 years old.I am a teacher and I teach Math.
Personクラスをabstract化しないまま、クラス内でabstractを使用したり、Personクラス内のabstractメソッドに処理を書こうとしたり、派生クラスで、abstractメソッドを書き忘れたりするとエラーが出る。
interfaceの使い方
interfaceとはオブジェクトのみのtypeエイリアス。
インターフェースはクラスが実装すべきフィールドやメソッドを定義した型
typeエイリアスとほとんど変わらないが、オブジェクトにのみinterfaceは使える。
interfaceがオブジェクトのみを扱うのでわかりやすいというメリットがあるらしい。
// typeエイリアス
type Human = {
name: string;
age: number;
};
const human: Human = {
name: "Quill",
age: 38,
};
let developer: Human;
// interface
interface Human {
name: string;
age: number;
};
const human: Human = {
name: "Quill",
age: 38,
};
let developer: Human;
interfaceにメソッドを追加する方法
例1)
interface Human {
name: string;
age: number;
greeting: (message: string) => void; // 追加
}
const human: Human = {
name: "Quill",
age: 38,
greeting(message: string) { // 追加
console.log(message);
},
};
例2)
interface Human {
name: string;
age: number;
greeting(message: string): void; // 変更
}
const human: Human = {
name: "Quill",
age: 38,
greeting(message: string) {
console.log(message);
},
};
implementsを使用して、クラスに対してinterfaceの条件を適応させる方法
class クラス名 interface インターフェース名 {}
とクラスを作成することで指定したinterfaceの条件を満たすクラスを作成する
interface Human {
name: string;
age: number;
greeting(message: string): void;
}
class Developer implements Human {
constructor(public name: string, public age: number) {}
greeting(message: string): void {
console.log(message);
}
}
implementsは複数指定でき、そこに定義されているものは全て実装する
class Developer implements Human, test, sample { // 複数のimplements
constructor(public name: string, public age: number) {}
greeting(message: string): void {
console.log(message);
}
}
「?」を使ってあってもなくてもいいプロパティやパラメーターの作成
プロパティやパラメーター、メソッドの後に?
をつけるとあってもなくても良いものになる。
interface Nameable {
name: string;
nickName?: string; // プロパティの後に?をつけるとあってもなくてもいいプロパティになる
}
const nameble: Nameable = {
name: 'Quill'
}