本記事ではTypeScriptの学習を行おうとしている初学者向けの内容となります。
前回の記事に引き続きTypeScriptの文法についてまとめていきます。
【前回の記事】
TypeScriptについて学習しよう② TypeScriptの文法 vol.1
前提
・Windows 11
・VSCode
・TypeScript Version 4.7.4
・Node v22.12.0
TypeScriptの導入については前々回の記事を参照してください。
TypeScriptについて学習しよう① TypeScriptの説明と導入方法
VSCodeのターミナルを使用してTypeScriptのコードを実行するにあたって、
実行の流れは都度下記のようになっています。
.tsファイル記述⇒ターミナルで「tsc」を入力してEnter⇒VSCodeにてhtmlファイル名を右クリックし「Open With Live Server」、もしくはエクスプローラーからhtmlファイルの実行(chromeまたはedge)
条件分岐
if
初歩的な条件分岐の構文です。if(条件式){処理}の構文で成り立ち、(条件式)を計算してtrueとなるかfalseとなるかを判断します。
if(条件式){
// 処理
}
【使用例】
let i = 10;
if(i > 1){
// iが1より大きいかどうか判断します
console.log(i + 'は1よりも大きいです');
}
条件式に合致(true)すればコンソール上で下記のように結果が表示されます。
このソースコードの場合、値を1以下にすると下記のように結果が表示されなくなります。
let i = 0;
if(i > 1){
// iが1より大きいかどうか判断します
console.log(i + 'は1よりも大きいです');
}
値が1以下の場合の分岐を追加したい場合は次のelseを使用します。
else
ifの条件式に当てはまらなかった場合に
if(条件式){
// 処理1
}else{
// 処理2
}
実際に先ほどのソースコードを変更してみましょう。
let i = 0;
if(i > 1){
// iが1より大きいかどうか判断します
console.log(i + 'は1よりも大きいです');
}else{
// iが1より小さいかどうか判断します
console.log(i + 'は1よりも小さいです');
}
無事1より小さい値の場合の処理が表示されました。この状態でたとえば値が1である場合はどのようになるか確認してみましょう。
let i = 1;
if(i > 1){
// iが1より大きいかどうか判断します
console.log(i + 'は1よりも大きいです');
}else{
// iが1より小さいかどうか判断します
console.log(i + 'は1よりも小さいです');
}
表示するとこのようになります。1は1であって、別に1より小さいわけではないですよね…。
値が1だった場合の分岐をもう一つ追加しなければならないときelse ifを用います。
else if
分岐を3つ以上に分けたいときに使用されます。
if(条件式1){
// 処理1
}else if(条件式2){
// 処理2
}else{
// 処理3
}
下記のように記述すれば値が1である場合の処理を記述することができます。
let i = 1;
if(i > 1){
// iが1より大きいかどうか判断します
console.log(i + 'は1よりも大きいです');
}else if(i = 1){
console.log('値は' + i +'です');
}else{
// iが1より小さいかどうか判断します
console.log(i + 'は1よりも小さいです');
}
結果は下記のように表示されます。条件i=1の処理が実行されていることが確認できました。
else ifを用いて4つ5つと分岐を増やしても問題ありませんが、
可読性の観点から次で説明するswitch文の使用が推奨されます。
switch文
複数の値を用意して、その値に一致した場合、該当する処理を行う条件分岐です。
switch (値){
case 値:
// 処理
case 値:
// 処理
case 値:
// 処理
case 値:
// 処理
default:
// 処理
}
例えば変数studentに「太郎」という人物の名前を格納して下記のコードを実行します。
let student = '太郎';
switch (student) {
case '太郎':
console.log('太郎');
break;
case '花子':
console.log('花子');
break;
case '次郎':
console.log('次郎');
break;
case '桜子':
console.log('桜子');
break;
default:
console.log('クラスに'+ student +'は存在しません');
}
次に、コード内の条件に存在しない生徒名「三郎」を変数studentに格納してコードを実行してみます。
分岐の中に「三郎」は存在しないので、defaultの分岐に入って処理が行われます。
null
…存在しない、あるいは無効を意味する値です。関数の戻り値として得られることがあります。
関数の戻り値として得られることがあります。(例:何らかの処理を行うが、該当するデータが存在しなかった場合など)
実際にnullとなる例を確認してみましょう。
// 呼び出し
nullCheck(null);
function nullCheck(value: any): void{
if (value == null){
console.log("nullです");
}else{
console.log("nullではありません");
}
}
undifined
…変数を宣言した後、何も値を代入していないときに入っている値です。未定義を意味します。
配列、オブジェクト、関数でもundifinedが出てきます。
・配列の要素に値が設定されていないとき
・オブジェクトのプロパティに値が設定されていないとき
・関数を呼ぶときに引数を省略→引数にundifinedが渡される
・関数の戻り値を設定していないとき→戻り値としてundifinedが返ってくる
実際にundifinedとなる例を確認してみましょう。
// 呼び出し
undefinedCheck(undefined);
function undefinedCheck(value: any): void{
if (value == undefined){
console.log("未定義です");
}else{
console.log("定義されています");
}
}
比較演算子
…値の比較において必要不可欠なのがこの比較演算子です。
比較演算子の種類は下記のようになっています。
演算子 | 説明 |
---|---|
A < B | 小なり演算子。AがBよりも小さければtrue。そうでなければfalse。 |
A <= B | 小なりイコール演算子。AがBよりも小さい、あるいは同じであればtrue。そうでなければfalse。 |
A > B | 大なり演算子。AがBよりも大きければtrue。そうでなければfalse。 |
A >= B | 大なりイコール演算子。AがBよりも大きい、あるいは同じであればtrue。そうでなければfalse。 |
if else文の例で出したものとほとんど同一ですが、このような使用例となります。
この例の場合、変数iに格納された3は1よりも大きいので、「3は1よりも大きいです」と結果が表示されます。
let i = 3;
if(i > 1){
// iが1より大きいかどうか判断します
console.log(i + 'は1よりも大きいです');
}else{
// iが1より小さいかどうか判断します
console.log(i + 'は1よりも小さいです');
}
論理演算子
…booleanの値(真偽値)を操作するために使用します。
主な論理演算子は下記です。
&& (AND(AかつB))
…AとBが両方trueである場合にtrueを返します。
|| (OR(AまたはB))
…AとBの少なくとも片方がtrueである場合にtrueを返します。
!(NOT(~ではない))
…trueだとすればfalseを返し、falseだとすればtrueを返します。
let a: boolean = true;
let b: boolean = false;
console.log(a && b); // false
console.log(a || b); // true
console.log(!a); // false
console.log(!b); // true
論理演算子には優先順位があり、下記の順番で優先されます。
(優先度高)「!」「&&」「||」(優先度低)
三項演算子
…別名条件演算子とも呼びます。三項演算子という名の通り、3つの演算対象を記述します。
「?」と「:(コロン)」を用いて、「条件」「trueのときの値」 「falseの時の値」を設定します。
[条件] ? [trueの時の値] : [falseの時の値]
下記のように使用することができます。
trueの場合
let test: string = 100 > 70 ? 'この数式は正しいです。' : 'この数式は間違っています';
console.log('trueの場合:', test);
falseの場合
let test: string = 70 > 100 ? 'この数式は正しいです。' : 'この数式は間違っています';
console.log('falseの場合:', test);
このように条件が真であるか偽であるかで、値を分けることができます。
1行で書き表せられるため、主にコードを簡略的に記述するために用いられますが、
慣れないうちはif文等を用いた方が可読性的にも良いでしょう。
繰り返し処理
プログラミングにおいて必要不可欠なのがこの繰り返し処理です。
繰り返し処理を記述することによって、都度同じ処理をするコードを羅列しなくても良くなり、コードの可読性が向上します。
また、〇回まで繰り返し処理を行ったら処理を終了する等、条件に合致するかどうかで処理の回数を制御できます。
for文
…指定回数の処理を行うのに適した繰り返し処理の記述方法です。
例えば、配列に格納した値を順番に取り出すなどの動作を行うために使用されることが多いです。
for([値の初期化];[条件];[変化式]){
// 繰り返し行いたい処理
}
下記のように使用します。
この例はiが10以上の数値になるまでカウントする処理です。
for(let i = 0; i<= 10; i++){
console.log(i);
}
while文
…この記述方法は、簡潔に繰り返し処理を記述したい場合などに用いられます。
内容としては条件式に当てはまっている間(trueである間)、処理を繰り返し行うという意味合いになります。
while([条件式]){
// 繰り返し行いたい処理
}
for文と異なり、値の初期化(ここでいうlet i = 0)はwhile文の外で行います。
使用例は下記のようになります。
let i: number = 5;
while(i > 0){
console.log(i);
i--;
}
do while文
…while文と似た書き方をします。while文との違いは、継続の判定を行うwhileを繰り返し処理の末尾に記述する部分です。判定が最後に行われるため、最低1回は処理が行われます。
do{
// 繰り返す処理
}while([条件式])
また、while文と同様に、値の初期化(ここでいうlet i = 0)はwhile文の外で行います。
使用例は以下です。
let j: number = 0;
do{
console.log(j);
j ++;
}while(j < 10)
for of 文
…Array、Map、Set、argument等の反復可能オブジェクトの値を得て、繰り返し処理を行うことができる記述方法です。forの後に記述している「(変数 of オブジェクト)」の構文を使用して、変数に値を格納します。
【用語解説】
Array(配列)
…複数の値を1つに格納できるデータ構造のことです。それぞれの値はインデックス(番号)でアクセスが可能です。(例: let a: number[] = [2,4,8,16];)
Map(マップ)
…キーと値の組み合わせを保持するデータ構造のことです。キーを使用して、それに対応する値にアクセスすることができます。(例: let map = new Map(); map.set("one", 1);)
Set(セット)
…一意の値を保持するデータ構造のことです。同じ値を複数回格納することはできません。(例: let set = new Set(); set.add(1); set.add(2);)
argument(引数)
…関数に渡される値のことです。関数は、関数の呼び出し元から渡されてきたこの引数を使用して処理を行います。(例: function greet(name: string) { console.log("Hello, " + name); } greet("Tarou");)
for ([変数] of [反復可能オブジェクト]){
// 繰り返す処理
}
使用例は下記のようになります。
const a: number[] = [2,4,8,16];
for (const arr of a){
console.log(arr);
}
このように配列に格納されていた数値をarrという変数に格納して順に表示することができました。
関数
関数について
…特定のタスクや計算を実行するためのコードのブロックのことです。再利用することができ、必要に応じて何度でも呼び出し可能です。
function命令
…TypeScriptやJavaScriptで関数を定義するための基本的な記述方法です。
function [関数名]([引数]: [型]): [戻り値の型] {
// 処理
}
ではここで、関数の定義と呼び出しを行ってみましょう。
// 関数の呼び出し
let sum = add(5, 3);
console.log(sum);
// 関数の定義
function add(a: number, b: number): number {
return a + b;
}
結果はこのようになります。呼び出し元で設定した引数「5」と「3」が関数に渡され、
関数add内で足し算の処理が行われて「8」がコンソールに出力されています。
ちなみにですが、TypeScriptにおいてはJavaScriptと同様、関数の呼び出しと定義の順番は重要ではありません。
上記の例だと、関数の呼び出し→関数の定義の順番で処理が行われていますが、これが逆になっても問題は有りません。
これは、JavaScript(TypeScript)のホイスティングと呼ばれる機能によるものです。
関数宣言や変数宣言がそのスコープの先頭に持ち上げられることを意味し、関数を定義する前に呼び出すことが可能となります。
ただし、後述する関数式やアロー関数の場合はホイスティングが適用されないため、定義の前に関数を呼び出そうとするとエラーが発生します。
関数リテラル
…別名関数式とも言います。関数を名前を設定しないまま定義し、その場で使用できるようにする記述方法です。関数リテラルは変数へ代入したり、他の関数に渡したりすることができます。
const 関数名 = function(引数1: 型, 引数2: 型, ...): 戻り値の型 {
// 関数の処理内容
return 戻り値;
};
使用例は下記のようになります。
const test = function(a: number, b: number): number{
return a + b;
};
let sum = test(5, 5);
console.log(sum);
先に関数を定義しておき、次に引数「5」と「5」を設定して関数を呼び出します。
呼び出し時点で変数sumを宣言していますが、test関数での処理結果をそのまま変数sumに格納するという内容になります。
最後にconsole.logで変数sumの中身を出力して、test関数の処理結果が表示されればOKです。
アロー関数
…先ほどのfunctionや関数リテラルよりも、短く簡潔に記述することができる関数の記述方法です。
(引数) => {
// 処理
};
使用例は以下のようになります。今回は引数を3つに増やし、aとbとcの合計値を求める処理を記述しました。
const test = (a: number, b: number, c:number): number => {
return a + b + c;
};
let sum = test(5, 5, 10);
console.log(sum); // 20が表示されます
コールバック関数
…先ほどのfunction、関数リテラル、アロー関数については、関数名を付けて関数を作成しましたが、関数は名前を付けずに作成することもできます。
名前のない関数は、名前での呼び出しが行えないため、変数に格納するよう記述するか、オブジェクトのプロパティに入れるか、関数の引数にするか、すぐにその場で使用しなければなりません。
この用途によって関数の呼び名が変わるので、軽く頭の隅に置いておくと良いでしょう。
・変数に格納するとき: 関数リテラルまたは無名関数
・オブジェクトのプロパティに入れたとき: メソッド
・関数の引数にするとき: コールバック関数
・すぐにその場で使用するとき: 即時実行関数
配列・オブジェクト・タプル
これらは複数の値を1つの変数にまとめて管理できる機能です。
配列
…複数のデータの集合です。1つの変数に対して複数の値を格納できます。
また、繰り返し処理を用いて値を1つずつ取り出すこともできます。
配列の中に入っているデータに対して、インデックスでアクセスすることができます。
イメージとしては、連なった箱の中にデータが入っているという状況です。
const scores: number[] = [100,90,20,30,40,60];
インデックス
…ここでのインデックスは「添え字」といい、ある要素を特定するためにつけられた数字(もしくは文字列)のことです。
配列におけるインデックスの考え方で注意すべきは、0始まりである ということです。
例えば一番最初の要素を取り出して表示したいときに、下記のように記述します。
const scores: number[] = [100,90,20,30,40,60];
console.log(scores[0]);
// 結果として100が出力される
配列の値の取り出し方
最初に配列は繰り返し処理を用いて値を1つずつ取り出すこともできますと述べましたが、下記にその例を記述しました。
const scores: number[] = [100,90,20,30,40,60];
// 配列scoreの個数を出力する処理
console.log(scores.length);
for(let i = 0; i < scores.length; i++){
console.log(scores[i]);
}
コンソールにはこのように出力されます。
一番最初の「6」はscores.lengthによって取得した、配列scores内に格納されている要素の個数を出力しています。
以降、繰り返し処理で配列scores内の要素を取り出し、出力しています。
(取り出し→出力という処理の流れを6回分行っていることになります。)
配列の宣言方法
型注釈
…配列の要素の方を明示的に指定して宣言する方法
const scores: number[] = [100, 90, 20, 30, 40, 60];
const animals: string[] = ['cat', 'dog', 'monkey'];
ジェネリック型を使用して宣言
ジェネリック型
…TypeScriptの機能です。コードの再利用性と型安全性を高めることを目的に使用されます。
ジェネリック型を使用すると、関数・クラス・インターフェースなどを特定の型に依存せずに記述することができます。
JavaScriptにこの機能は存在しませんが、TypeScriptでジェネリック型を使用してコードを記述し、そのコードをJacaScriptにコンパイルすることはできます。JavaScriptへのコンパイル後は、型情報が削除され、通常のJavaScriptとして動作します。
function identity<T>(arg: T): T {
return arg;
}
【一行目の解説】
・function identity<T>
この部分でidentityという名前の関数を宣言し、Tというジェネリック型パラメータを設定しています。
ジェネリックパラメータは関数が呼び出されるときに具体的な方に置き換えるものです。
・(arg: T):
…関数の引数であるargの型を**T**として設定している。Tは上記の通りジェネリックパラメータであるため、任意の方を受け取ることができます。
・T:
…関数の戻り値の方をTとしています。これにより、引数と同じ型の値を返すことが保証されます。
複数の方を持つ配列の宣言
…下記のように記述した場合、配列の要素が複数の型を持つことができます。
let mixed: (number | string)[] = [1, "two", 3, "four"];
変数mixedを宣言し、**(number | string)[]**の部分でnumber型もしくはstring型の要素を持つ配列であることを示しています。上記の例では口語にnumber型、string型と並んでいますが、交互に格納しなければならないというわけではありません。number型、string型どちらかの要素が格納されていれば良いです。
例えば下記のように要素を追加する場合、変数の宣言時に指定したnumber型・string型どちらの型でも問題ありません。
let mixed: (number | string)[] = [1, "two", 3, "four"];
mixed.push(5); // 変数mixedに5を格納
mixed.push("six"); // 変数mixedに文字列のsixを格納
for(let i = 0; i < mixed.length; i++){
console.log(mixed[i]);
}
配列の初期化
…空の配列を宣言する場合のことを言います。既存の配列を空にする場合にも用います。
今まで値を格納していた**[];**の中に何も入れない状態で記述します。
let emp: number[] = [];
オブジェクト(連想配列)
…オブジェクトは、キーと値の組み合わせで管理するデータ構造です。この概念についてはJavaScriptとTypeScriptどちらにおいても共通しています。
下記のように使用します。
let student = {
name: 'Tarou',
age: 15,
class:"A組"
};
変数studentの中に、name, age, classというキー(key・プロパティ名) があり、それに対応して"Tarou", 15, "A組" がそれぞれのキーの値(value) となります。
オブジェクト(連想配列)はこのように一見して何が何の値なのか、データの関連が分かりやすいように格納する方法です。
値の取り出し方は下記のように行います。
値を取り出す場合は、取り出したい値のキーを呼び出します。呼び出し方は2通りあります。
console.log(student.name); // キーであるnameを呼び出して値を取り出す
console.log(student['age']); // キーであるageを呼び出して値を取り出す
値の変更方法
変数studentのnameの値を変更したい場合は下記のように記述します。
let student = {
name: 'Tarou',
age: 15,
class:"A組"
};
// 値の変更前にTarouが出力されることを確認
console.log(student.name);
// nameの値を変更
student.name = 'Hanako'
// 値の変更後はHanakoが出力されることを確認
console.log(student.name);
「Tarou」から「Hanako」に値が変更されていることが確認出来たらOKです。
キー(プロパティ)の個数を求める
ここでなぜ突然キーの個数を求めるのかというと、キーの個数分、値を取り出す繰り返し処理を行うために必要です。
普通の配列と同様、繰り返し処理で値を順番に取得することができます。
まずキーの個数は下記のように求めます。
let student = {
name: 'Tarou',
age: 15,
class: 'A組'
};
console.log(Object.keys(student).length);
コンソール上にキーの個数が表示されていることが確認できます。
キー(プロパティ)を確認する
設定したキーの一覧を取得するための記述が存在します。例えばオブジェクトのキーが処理内で動的に追加されたり削除されたりする場合、キーの一覧を取得することで現在のオブジェクトの状態を確認することができます。その場合のデバッグにうってつけの記述です。
プロパティの確認をするコードは下記のようになります。
let student = {
name: 'Tarou',
age: 15,
class:'A組'
};
console.log(Object.keys(student));
コンソールの表示を見ると、ソース内で設定したキーが表示されていることが確認できます。
値の型の明示方法
ちなみに先ほどは記述していませんでしたが、TypeScriptなので値の型を明示することができます。
その明示方法は下記のようになります。
先にキーにそれぞれ対応する型を記述して、そのあとに値を設定するという記述方法になります。
let student: {
name: string,
age: number,
class: string
} = {
name: 'Jiro',
age: 16,
class:"B組"
};
console.log(student.name);
console.log(student.age);
console.log(student.class);
TypeScriptは型チェックを行ってコードの保守性を保つため、変数の初期値や代入時に型が示している条件に合わないオブジェクトを入れようとした場合コンパイルエラーとなり、型の相違をコーディング時点で検出することができます。
タプル
…固定された数と型の要素を持つ配列のことです。JavaScripにはない、厳密な配列の型の定義方法です。
各要素の型、個数、順序が厳密に定義されます。
先ほど**オブジェクト(連想配列)**の例で挙げた記述を、タプルに変更してみましょう
// オブジェクト(連想配列)の書き方
let student = {
name: 'Tarou',
age: 15,
class:'A組'
};
タプルに変更すると下記のようになります。
オブジェクト(連想配列)とは異なり、キー(プロパティは設定しません。)
let student: [string, number, string] = ['Tarou', 15, 'A組'];
console.log(student[0]);
console.log(student[1]);
console.log(student[2]);
値の変更方法
値を上書きする場合は下記のように要素の個数をデータ型を一致させる必要があります。
let student: [string, number, string] = ['Tarou', 15, 'A組'];
console.log(student[0]);
console.log(student[1]);
console.log(student[2]);
// 値の変更
student = ['Hanako', 14, 'B組'];
console.log(student[0]);
console.log(student[1]);
console.log(student[2]);
コンソールには下記のように表示され、配列の値が上書きされたことを確認できます。
もし、最初にタプルを定義する際に設定した型とは異なる型を持つ要素を代入しようとすると下記のようにエラーが表示されます。
また、定義時の個数と異なる個数の要素を代入しようとすると、下記のようにエラーが発生します。
このようにコーディング時点で警告を発生させてエラーを検知できるのがTypeScriptを使用する利点です。
クラス
クラスについて
…同じプロパティやメソッドを持ったオブジェクトを複数作成できる仕組みのことです。これらは再利用可能、つまりそのクラスのインスタントを作成してメソッドを呼び出すことができます。
クラスの宣言
クラス名を設定するとき、頭文字は大文字にします。
下記のようにプロパティ(name, age, class)、型(string, number)、初期値('', 0 , '')を設定します。
初期値を設定しないとエラーが発生するため、注意してください。
class Student {
name: string='';
age: number = 0;
class: string='';
}
次にnew演算子を用いてクラスをインスタンス化します。
【用語解説】
インスタンス化
…クラスの定義に基づいて、具体的なオブジェクト(インスタンス)を作成するプロセスのこと。クラスはオブジェクトの設計図のようなもので、インスタンス化することでその設計図に従った実際のオブジェクトを生成します。
class Student {
name: string='';
age: number = 0;
class: string='';
}
// newでStudentクラスのインスタンスを作成し、変数taroに代入
const taro = new Student();
console.log(taro.name);
console.log(taro.age);
console.log(taro.class);
コンソールには下記のように表示されます。まだ中身が初期値のままなので空文字と0が表示されています。
次に、各プロパティに値を与えてからconsole.logを実行してみます。
class Student {
name: string='';
age: number = 0;
class: string='';
}
// newでStudentクラスのインスタンスを作成し、変数taroに代入
const taro = new Student();
// 値の代入
taro.name = 'Tarou';
taro.age = 16;
taro.class = 'A組';
console.log(taro.name);
console.log(taro.age);
console.log(taro.class);
オプショナルなプロパティ
…オプショナルなプロパティとは…?となりますが、必須ではなく、存在してもしなくても良いプロパティということです。つまり任意のプロパティということですね。
オプショナルなプロパティは下記のように設定します。
class Student {
name: string='';
age?: number; // 任意なので一般的には初期値を設定しません
class?: string; // 任意なので一般的には初期値を設定しません
}
先ほどと同じコードの内容ですが、今回はageとclassのプロパティ名の後にクエスチョンマークを付けています。
このように設定することで、ageプロパティとclassプロパティが存在しなくてもエラーが発生しません。
class Student {
name: string='';
age?: number;
class?: string;
}
const taro = new Student();
// 値の代入
taro.name = 'Tarou';
taro.age = 16;
console.log(taro.name);
console.log(taro.age);
console.log(taro.class);
コンソールには下記のように表示されます。
Studentクラス内のclassプロパティは値を代入していないのでundifinedという文字が表示されています。
読み取り専用プロパティ
…先ほどのオプショナルなプロパティに続き、プロパティって色々設定できるようで。
次はプロパティを読み取り専用にする記述方法をご紹介します。
classの定義時に、nameプロパティに「Hanako」という値を入れ、プロパティ名の前にreadonlyを記述しておきます。
class Student {
readonly name: string='Hanako';
age?: number;
class?: string;
}
const taro = new Student();
// 値の代入
taro.name = 'Tarou';
taro.age = 16;
console.log(taro.name);
console.log(taro.age);
console.log(taro.class);
読み取り専用となっているnameプロパティの値が上書きできないことが確認できました。
このように値の内容を変更したくないプロパティが存在する場合、読み取り専用にすることで不用意に内容を上書きするのを防ぐことができます。
パブリックプロパティ
…オプショナル・読み取り専用の次にパブリックなプロパティときました。その名の通り公開されているプロパティとのことで、デフォルトでどこからでもアクセスが可能なプロパティです。
classの定義時に、プロパティ名の前にpublicを記述しておきます。
class Student {
public name: string='';
age?: number;
class?: string;
}
ただ、TypeScriptにおいてプロパティはデフォルトで全てパブリックなので、省略したままでもよいです。
可読性や意図を明確にするためにpublicを記述しておくこともあるようです。
プライベートプロパティ
…公開があれば非公開があるということで次はプライベートプロパティについてのご紹介です。
このプライベートプロパティはクラスの外部からアクセスができない、クラス内部でのみアクセス可能なプロパティです。
年齢をあまり公表したくないとのことで、ageプロパティをprivateにします。(そういう話じゃない)
class Student {
name: string='';
private age: number;
class?: string;
constructor(name: string, age: number, className?: string) {
this.name = name;
this.age = age;
this.class = className;
}
// 内部から年齢を得ようとするメソッド
getAge(){
return this.age;
}
}
// 名前と年齢を渡す
const hanako = new Student('Hanako', 25);
// プライベートプロパティであるため、外部から取得しようとするとエラーが発生する
hanako.age = 25;
console.log(hanako.name); // "Hanako" と表示される
console.log(hanako.getAge()); // Studentクラス内部で定義されているgetAgeメソッドを利用しているので25 と表示される
console.log(hanako.age); // プライベートプロパティであるため、外部から取得しようとするとエラーが発生する
代入時点のエラーメッセージをVSCodeで確認するとこのような感じになります。
また、console.logでhanako.ageを記述して直接年齢の値を取ってこようとしているコードにもこのようにメッセージが表示されます。
エラーが発生しないようにconsole.loでhanako.ageを代入・取得しようとしている箇所をコメントアウトして実行すると問題なく実行できます。
このように内部で定義したgetAgeメソッドを通ってでないと年齢が取得できないという動作になります。
メソッド
…クラスやオブジェクトの中で定義される関数のことです。メソッドはオブジェクトのプロパティとして定義され、オブジェクトの動きを定義するために使用されます。(語弊があるかもしれませんが、とある特定の処理を行う部品としての認識を持っていただければと思います。)
メソッドの特徴
・クラス内で定義
…メソッドはクラス内で定義され、クラスのインスタンスによって呼び出されます。
・thisキーワード
…メソッド内でthisを使用することで、クラスのインスタンスのプロパティや
他のメソッドにアクセスすることができます。
・アクセス修飾子(アクセシビリティ修飾子)
…メソッドにpublic、privateなどのアクセス修飾子を付与することができます。
デフォルトではpublic(クラス内であればどこからでもアクセス可能)です。
それではメソッドの一例を確認してみましょう。
class Student {
name: string='';
age: number;
class: string;
}
}
コンストラクタ
…newによってインスタンスが作成される際に呼び出される関数で、クラスのインスタンスが生成されるときに呼び出される特殊なメソッドのことです。
コンストラクタはインスタンスの初期化を行うために使用され、通常はプロパティに初期値を設定したり、必要な初期化処理を行います。
初期化が必要な理由
・一貫性の確保
…クラスのインスタンスが常に正しい状態で作成されるようにするためです。
初期化処理を行うことによって、インスタンスのプロパティが適切な初期値を
持つことができます。例えばクラスやメソッドは再利用可能なので、それらを
最後に呼び出したときに結果として得られていた値がそのまま残っていたりすると、
正確な結果が得られない可能性があります。
・エラー防止
…初期化されていないプロパティにアクセスしようとするとエラーが発生する可能性が
あるためです。
・依存関係の設定
…クラスのインスタンスが他のオブジェクトやリソースに依存している場合、
初期化処理でこれらの依存関係を設定することができます。
・コードの可読性と保守性の向上
…インスタンスの初期化処理がコンストラクタに集約されるため、コードの可読性と保守性が向上します。
コンストラクタは下記のように使用します。
class Student {
name: string='';
readonly age: number;
class?: string;
// コンストラクタ
constructor(name: string, age: number, className?: string) {
this.name = name;
// コンストラクタはreadonly(読み取り専用)のプロパティにも代入することができる。
this.age = age;
this.class = className;
}
}
const hanako = new Student('Hanako', 25, 'B');
console.log(hanako.name);
console.log(hanako.age);
console.log(hanako.class);
クラスの型
…TypeScriptにおいて、クラス宣言をすると、クラスオブジェクトという値を作成し、インスタンスの型が宣言されます。
class Student {
name: string='';
readonly age: number;
class?: string;
// コンストラクタ
constructor(name: string, age: number, className?: string) {
this.name = name;
// コンストラクタはreadonly(読み取り専用)のプロパティにも代入することができる。
this.age = age;
this.class = className;
}
public checkAge(): boolean {
return this.age >= 15;
}
}
// 変数hanakoはStudent型になる(number型や string型と同じように使用することができる)
const hanako = new Student('Hanako', 25, 'B');
// 型注釈の記述
const ziro: Student = new Student('Ziro', 18, 'C' );
console.log(hanako.name);
console.log(hanako.age);
console.log(hanako.class);
console.log(ziro.checkAge());
クラスの継承
…TypeScriptはJavaScript同様クラスの継承を行うことができます。継承で新しいクラスを作成したとき、もとになったクラスは親、新しく作成したクラスを子と呼びます。継承の用途は機能の追加・拡張です。
下記の例ではStudentクラスを親とし、SpecialStudentを子とします。
class Student {
name: string='';
readonly age: number;
class?: string;
// コンストラクタ
constructor(name: string, age: number, className?: string) {
this.name = name;
// コンストラクタはreadonly(読み取り専用)のプロパティにも代入することができる。
this.age = age;
this.class = className;
}
public checkAge(): boolean {
return this.age >= 15;
}
}
// extendsを記述して、SpecialStudentクラスがStudentクラスの子クラス(いわゆる拡張機能)であることを表します。
class SpecialStudent extends Student {
}
// Studentクラスの機能を継承したSpecialStudentクラスのインスタンスを作成
const koziro = new SpecialStudent('koziro', 10, 'D');
console.log(koziro.name);
console.log(koziro.age);
console.log(koziro.class);
// 親のメソッドを使用することもできる
console.log(koziro.checkAge());
結果は下記のように表示されます。SpecialStudentクラスがStudentクラスの機能を継承していることが分かります。
オーバーライド
…親クラスで定義されたメソッドを子クラスで再定義することを言います。わかりやすくいうと、親クラスの機能の内容を子クラス内で上書きするということです。これによって子クラスで特定の動作をカスタマイズすることができます。
下記のような例を記述します。
class Student {
name: string = '';
readonly age: number;
class?: string;
constructor(name: string, age: number, className?: string) {
this.name = name;
this.age = age;
this.class = className;
}
// このメソッドをオーバーライドする
public checkAge(): boolean {
return this.age >= 15;
}
}
class SpecialStudent extends Student {
specialStudentNumber: number = 10;
public tellSpecialStudent() {
console.log(`${this.specialStudentNumber} です`);
}
// どんな数値がメソッド内にきてもtrueが返ってくるようにオーバーライド
public checkAge(): boolean {
return true;
}
}
const setsuko = new SpecialStudent('Setsuko', 5, 'A');
console.log(setsuko.checkAge());
checkAgeメソッドの内容が子クラスで上書きされ、コンソールには「true」という結果が表示されます。
コンストラクタのオーバーライド
…次にコンストラクタのオーバーライドについてご紹介します。下記の例では親クラスのコンストラクタで値の初期化を行っていますが、それを子クラスのコンストラクタで初期設定を上書きすることができます。
class Student {
name: string = '';
readonly age: number;
class?: string;
// オーバーライドするコンストラクタ
constructor(name: string, age: number, className?: string) {
this.name = name;
this.age = age;
this.class = className;
}
public checkAge(): boolean {
return this.age >= 15;
}
}
class SpecialStudent extends Student {
specialStudentNumber: number = 10;
// コンストラクタのオーバーライド
constructor(name: string, age: number, specialStudentNumber: number) {
super(name, age);
this.specialStudentNumber = specialStudentNumber;
}
public tellSpecialStudent() {
console.log(`${this.specialStudentNumber} です`);
}
// どんな数値がメソッド内にきてもtrueが返ってくるようにオーバーライド
public checkAge(): boolean {
return true;
}
}
// 引数で必要なのはstring型、number型、number型に変更になったため、引数の内容を変更
const setsuko = new SpecialStudent('Setsuko', 5, 3);
console.log(setsuko.name);
console.log(setsuko.age);
console.log(setsuko.specialStudentNumber);
console.log(setsuko.checkAge());
モジュール
モジュールについて
…TypeScriptの変数、関数、クラス等を効率よく再利用する仕組みのことです。
export宣言
モジュールのexport宣言は下記のように行います。
下記の例では機能をファイル単位で分けているという認識でいてください。
export const name = 'Taro';
export const age = 18;
このように記述することでtaro.tsはnameとageをエクスポートするモジュールとして扱うことができます。
import宣言
次に、エクスポートされたnameとageをインポートするために下記のような記述をapp.tsファイルに記述します。
モジュール名部分(パスの部分)は相対パスで記述します。
import {name, age} from './taro.js';
console.log(name, age);
もしコンソール上でインポートができないというエラーが発生した場合はtsconfig.jsonファイルを開き、「common.js」と表示されている行をコメントアウトします。
次に、htmlファイルを開き、scriptタグでapp.jsを取り込むソースコードの中に type="module" を記述します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TypeScript Test</title>
<script type="module" src="dist/app.js"></script>
<script src="dist/app_second.js"></script>
</head>
<body>
</body>
</html>
これによってtaro.tsファイルからnameとage2つの変数をエクスポートし、app.tsファイルへインポートして利用することができます。
ちなみにエクスポート宣言は次に下記のようにまとめて記述することもできます。
const name = 'Taro';
const age = 18;
export {name, age};
また、変数名に別名を付けてエクスポートすることもできます。
const name = 'Taro';
const age = 18;
export {name as namae, age as toshi};
上記のようにエクスポート側で変数に別名を付けた場合は、インポート側で別名を使用する必要があります。
import {namae, toshi} from './taro.js';
console.log(namae, toshi);
関数のexport
…先ほどは変数のエクスポートを行いましたが、関数のエクスポートやクラスのエクスポートも行うことができます。
まず、エクスポート側であるtaro.tsを下記のように編集します。
export const getTaroName = () =>{
return 'Taro';
};
次にインポート側であるapp.tsを下記のように編集して正しくインポートできるようにします。
import { getTaroName } from './taro.js';
console.log(`${getTaroName()}さん、こんにちは`);
クラスのexport
クラスのエクスポートは下記のように記述して行います。
クラスの定義の前にexportを記述します。
export class Student {
name: string = '';
age: number = 0;
class: string = '';
isAgeCheck(): boolean{
return this.age >= 10;
}
}
次にインポート側であるapp.tsを下記のように編集して正しくインポートできるようにします。
import { Student } from './taro.js';
// インスタンスを作成
const student = new Student();
student.name = 'Taro';
student.age = 0;
student.class = 'A';
console.log(student.name);
console.log(student.age);
console.log(student.class);
console.log(student.isAgeCheck());