TypeScritp基礎のメモ
記事にするほどではない(と思う)内容をここに書く。
型注釈と型推論
型注釈
明示的に型を指定すること。変数名: 型名
let myouji: string = "苗字";
型推論
初期値に基づいて型を推論する。
let myouji = "苗字"; //初期値からstring型と推論
let nenrei = 100; //初期値からnumber型と推論
let isBoolean = true; //初期値からboolean型と推論
let sampleArray1 = ["国名","県名","市区町村名"]; //初期値からstring型の配列と推論
let sampleArray2 = ["地名", 100-1] //初期値からstring型 | number型の配列と推論(すごい!)
let sampleArray3 = ["文字", 99999, true]; //初期値からstring型 | number型 | boolean型の配列であることを推論(!?!?)
暗黙的な型変換
let sampleNum = 5 + true;
console.log(sampleNum);
これを実行すると、コンソールには6
が出力される。
JavaScriptでは、True
を暗黙的に型変換して数値の1
として扱う。
よって、5 + true
は5+1
となる。(ちなみにfalse
は0
として扱う)
TypeScriptでは、暗黙的な型変換を行わないためエラーとなる(error:演算子 '+' を型 'number' および 'boolean' に適用することはできません。
)
リテラル型
変数の値を具体的な値に限定する。
const seijin = 18;
を例にすると、seijin
の型名が18
になっている。これがリテラル型。
(seijin
は18
以外の値を持つことができない。)
let
でもリテラル型を宣言できる。
//Boolean
let literalBool: true = true;
//string
let literalString: `sampleString`;
リテラル型にリテラル型以外の値を代入しようとするとエラーとなる。
ユニオン型
変数に複数の型を指定する。
ユニオン型を整形する型(number | string |...
の部分)は、「メンバー」と呼ぶ。
//number型 と string型 を id に指定
let id: number | string;
メンバーに指定した型の値のみを代入できる
id = 10; //number型だからOK
id = "10"; //string型だからOK
メンバーに指定していない型の代入はエラー
id = true; //boolean型はメンバーに存在していないからエラー
ユニオン型とリテラル型を組み合わせると・・・
リテラル型に指定した型のみを変数に代入できるようになる。
let size: "small" | "medium" | "large";
size = "small";
VSCodeのコード補完にも、メンバーが表示される。
メンバーに存在しない値をセットしようとすると、エラーになる。
let size: "small" | "medium" | "large";
size = "mettya dekkaino"; //メンバーに存在しないからエラー
型エイリアス
特定の型に名前を割り当てて、再利用できるようにする。(予約語は使用できない)
パスカルケース
で記述されるのが慣例。
// number型 と string型 のユニオン方に ShouhinCode という名前を付ける
type ShouhinCode = number | string;
// リテラル型のユニオン型に Yakushoku という名前をつける
type Yakushoku = "shacho" | "kacho" | "hira";
let shohin:ShouhinCode; // 変数 shohin に ShouhinCode型 を指定
ShohinCode
に存在しない型を代入しようとするとエラーになる。
型エイリアスのユニオン型
型エイリアスはほかの型と組み合わせて使用することも可能。
宣言する際は、使用する順番に宣言しなくてもOK。
type Pc = Windows | Mac; //Windows型 と Mac型のユニオン型 PC を宣言
type Windows = "note" | "desktop";
type Mac = "MackBook" | "iMac" | "Mac Stuio";
let myPc: Pc = "desktop"; // 型を指定するときは、Windows型 と Mac型どちらも指定できる
もちろん、Pc
型に存在しない型は指定できない。
ちなみに・・・
jisaku
という新たな型を作成。
Pc
型とjisaku
型のユニオン型にPc2
という名前を付けて宣言することも可能。
この時、指定できる値はWindows
型、Mac
型、jisaku
型の3種類。
オブジェクト型
基本
string
型を持つname
プロパティと、number
型を持つseitoNo
プロパティを宣言。(ここではTypeScriptが型推論をしてくれている)
変数名はseito
とする。
const seito = {
name: "alice",
seitoNo: 99,
};
seito
に存在しないプロパティを指定するとエラー。
//プロパティ 'address' は型 '{ name: string; seitoNo: number; }' に存在しません。
console.log(seito.address);
seitoNo
プロパティにstring
を代入しようとするとエラー。
//型 'string' を型 'number' に割り当てることはできません。
seito.seitoNo = "30";
型注釈によるオブジェクトの生成
オブジェクト型を型注釈で指定するのは、変数名に次のように記述する。
型を指定したら、=
で値を代入する。
let notebook: {
title: string;
owner: string;
editionNumber: number;
} = {
title: "国語",
owner: "太郎",
editionNumber: 5,
};
これだと変数が増えてきたときに非常に読みにくくなってしまうから、型エイリアスを使うことによってすっきりさせることとができる。
type Notebook = {
title: string;
owner: string;
editionNumber: number;
};
const JapaneseNote: Notebook = {
title: "国語",
owner: "太郎",
editionNumber: 5,
};
今までの型宣言と同様に、コード補完にはNotebook
で指定した型しか表示されない。
また、存在しない型を指定しようとするとエラーになる。
プロパティが不足している場合
代入されるオブジェクトにが不足している場合は、TypeScriptがエラーを報告する。
次の例では、editionNumber
をコメントアウトしているためエラーとなる。
const physicsNote: notebook = {
title: "物理",
owner: "次郎",
// editionNumber: 2, コメントアウト
}
ネストされたオブジェクト
type Shcool = {
gakunen: number;
kousha: string;
floor: number;
classroom: {
classCode: number;
tannin: string;
ninzu: number;
};
};
ネストされているオブジェクト型を型エイリアスで書き換えることも可能。
type Classroom = {
classCode: number;
tannin: string;
ninzu: number;
};
type Shcool2 = {
gakunen: number;
kousha: string;
floor: number;
classroom: Classroom; //型エイリアスを指定
};
変数を宣言するときは次のようになる。
let KinjonoGkko:Shcool2 = {
gakunen:999,
kousha:"第○○校舎",
floor:99,
classroom:{
classCode:1,
tannin:"xx先生",
ninzu:999
}
}
もちろん、一部をコメントアウトしていたらエラー。
過剰プロパティチェック
変数mathTeacher
に代入されているオブジェクトが型定義にないseibetsu
プロパティを所持しているときはエラーになる。
type Kyoshi = {
name: string;
age: number;
};
//OK
const japaneseTeacher: Kyoshi = {
name: "国語の先生",
age: 999,
};
//エラー
const mathTeacher:Kyoshi = {
name: "数学の先生",
age: 999,
seibetsu:"female"
//型 '{ name: string; age: number; seibetsu: string; }' を型 'Kyoshi' に割り当てることはできません。
オブジェクト リテラルは既知のプロパティのみ指定できます。'seibetsu' は型 'Kyoshi' に存在しません。
}
あらかじめ、オブジェクトを生成し変数mathTeacher
に代入。
その後、Kyoshi
型として宣言した新しい変数を代入すると・・・?
const physicsTeacher = {
name: "物理の先生",
age: 999,
seibetsu: "female",
};
const physicsTeacherKoshi: Kyoshi = physicsTeacher;
physicsTeacher
はseibetsu
プロパティを所持しているが、エラーにはならない。
既存のオブジェクトを別の変数に代入する場合は、過剰なプロパティチェックを行わない。
オプショナルプロパティ
オブジェクトのプロパティに?
を追加することによって、任意(オプショナル)のプロパティとして宣言することができる。
変数にオプショナルプロパティが存在しなくてもエラーにはならない。
type Yakushoku = {
yakuName: string;
teatcherNmae: string;
gakunen?: number;
};
const sensei: Yakushoku = {
yakuName: "一般",
teatcherNmae: "先生",
gakunen: 2,
};
const kocho: Yakushoku = {
yakuName: "校長",
teatcherNmae: "なまえだよ",
//gakuen は存在しないがエラーにはならない
};
オプショナルプロパティとしたgakunen
はnumber
型とundefined
型のユニオン型として扱われる。
?
を使用せずに、number | undefined
と明示的に指定するとオプショナルプロパティとして省略することはできない。
type Yakushoku2 = {
yakuName: string;
teatcherNmae: string;
gakunen: number | undefined;
};
const sensei2: Yakushoku2 = {
yakuName: "校長",
teatcherNmae: "なまえだよ",
//gakuen: 1
};
読み取り専用
readOnly
キーワードの宣言によって、後から変更することができないプロパティを宣言できる。
type rekidaiSchoolName = {
readonly name: string;
year: number;
};
const schoolName: rekidaiSchoolName = {
name: "初代の名前",
year: 2000,
};
//yearの変更が可能
schoolName.year = 1998;
//nameは読み取り専用のため変更不可
schoolName.name = "間違えた名前を上書き";
Array型とTuple型
Array型
他のプログラミング言語同様の配列。
これまで同様に、型を指定しなければ型推論する。
const suuti = [1, 2, 3, 4, 5];//number[]型(型推論)
const kojinNames = ["太郎", "次郎", "三郎"];//string[]型(型推論)
指定された型以外の値を配列に格納しようとするとエラー。
suuti.push("6");
型注釈を行うには変数名:型名[]
とする。
let fruits: string[] //型注釈によるArray型の指定
fruits = ["Apple", "Grape", "Banana", "Peach", "pear"];
console.log(fruits[0].toUpperCase()); // → APPLE
Tuple型
TypeScriptには、JavaScripには存在しないTuple(タプル)
という型が存在する。
固定された長さを持ち、各要素に対して特定の型が指定できる。
コンパイル時には、JavaScriptの配列(Array
)に変換される。
文法は変数名:[型名1,型名2...]
次の例は、loginStatus
の0'番目が
sting型、
1番目が
boolean`型のTuple型。
let loginStatus: [string, boolean] = ["ログイン名", true];
変数loginStatus
はTuple
型で定義されていて、2つの要素を持つことが指定されている。
3つ目の要素としてログイン回数の数値を代入しようとすると型の不一致によるエラーとなる。
Tuple
型に存在しないインデックスにアクセスしようとするとエラー。
ラベル付きのTuple型
Tuple
にラベルを付与することによって、各要素がどんなデータを表しているかを直接確認できるようになる。
もちろん、指定している型と異なる型を代入しようとするとエラーになる。
type ShingoColor = [red: number, green: number, blue: string];
const shingo: ShingoColor = [1, 2, 'three'];
const shingo2: ShingoColor = [1, 2, 3];
ラベル末尾に?
を付けることによって、該当の要素をオプショナル(任意)として宣言可能。
さらに、スプレッド構文...
を使って、複数の要素を含むことが可能。
type TupleSample = [first: number, second?: string, ...rest: any[]];
let data1: Foo = [1]; //first要素のみの配列を代入
let data2: Foo = [1, "hello"]; //firstとsecond要素のみを代入
let data3: Foo = [1, "hello", true, 10, "world"]; //first,second要素の他に、...restに複数の要素を割り当て
インターセクション型
組み合わされたすべての型の特性を持つ。記法としては「AかつB」の型だと考えるとよい。
type MultiType = TypeA & TypeB & TypeC;
//ダミー
type TypeA = {
type_a: string
}
type TypeB = {
type_b: number
}
type TypeC = {
type_c: boolean
}
Cpu
型とMemory
型の2つの型をインターセクション型を使って結合して、Pc
型を作成。
Pc
型は、Cpu
型とMemory
型の両方がもつプロパティを含む必要がある。
type Cpu = {
cpu: string
core: number
}
type Memory = {
memorySize: number;
}
type PcType = Cpu & Memory;
const myPc: PcType = {
cpu: 'intel',
core: 8,
memorySize: 16
}
片方の型を持つプロパティが指定されていなければエラー。
const friendPc: PcType = {
cpu: 'intel',
core: 8,
}
VSCodeの候補にPcType
型が表示される
console.log(`cpu: ${myPc.cpu} core: ${myPc.core} memory: ${myPc.core}`);// cpu: intel core: 8 memory: 16
any型
型の制約なしに任意の値を受け入れる型。
any
型の変数には型チェックが適用されない。
let anyValue1: any = 1; //any型
anyValue1 = "noTypeCheck"; //any型
anyValue1.oncheck(); //型チェックが行われないのでエラーにならない
コンパイル自体は問題なく通るが、実際に処理されるタイミングでエラーとなる。anyValue1.oncheck()
において、以下のようなエラーが発生する。
value1.oncheck();
^
TypeError: value1.oncheck is not a function
unknown型
型の安全性を維持しながら未知の型の値を扱うことができる型
any
型と同じように、どんな値でも代入できるようになるが、unknown
型は変数の型チェックを無視するわけではない。
let unknownValue1: unknown = 1; //unknown型
number型などの型にunknown型を代入することはできない
しかし、unknown
型にunknown
型を代入することは可能(同一の型だから)
let unknownValue3: unknown = unknownValue1;
unknown型の算術演算子
算術演算子は許可されていない。
let sumValue1 = unknownValue1 + 5;
let sumValue2 = unknownValue1 * 5;
unknown
型の変数の操作は限られているが、比較演算子は許可されている。(==
,!=
,===
,!==
,>
,<
,>=
,<=
,)
let unknownValue4: unknown = 'a';
let unknownValue5: unknown = 'a';
if (unknownValue4 === unknownValue5) {
console.log('OK');
} else {
console.log('NG');
}
>> OK
unknown
型の変数が「一体何の型なのか」を確認し、確認がとれたらその変数を確認した型
として扱うことができる。
関数と型
TypeScriptでは型推論の機能があるので、「関数でも型推論してくれるだろう」という感覚で関数addNumber
を定義するとエラーになる。
function addNumbers(a, b) {
return a + b;
}
なぜならaddNumbers
関数の定義から、パラメータa
,b
は初期化されておらず型推論のヒントがないから。
このパラメータa
,b
の型は、関数を書いた人しか正しく推論できない。
関数が受け取る引数が数値であることを保証するために、パラメータa
,b
に明示的に型を指定する。
文法は:型名
。
function addNumbers(a: number, b: number) {
return a + b;
}
また、戻り値にも型を明示的に指定することが可能。文法は関数名(パラメータ):戻り値の型
。
function addNumbers(a: number, b: number): number {
return a + b;
}
関数型
次の例では、変数sampleFunction
に関数型を指定している
sampleFunction
はnumber
型のhikisu1
とstring
型のhikisu2を受け取り、
boolean`型の値を返却する関数型となる。
let sampleFunction: (hikisu1: number, hikisu2: number) => boolean;
変数の型として、関数型を指定しその変数にアロー関数を代入する場合は・・・
const arrowVariable: (hikisu1: number, hikisu2: number) => number = (a, b) => a + b;
分解してみる
- 関数型の定義
「hikisu1
とhikisu2
はどちらもnumber
型であり、戻り値もnumber
型である」ということを示す。
(hikisu1: number, hikisu2: number) => number
- アロー関数
(a, b) => a + b
「引数にa
とb
を受け取り、それを足し算して戻り値として返却する」ということを示す。
5. 変数arrowVariable
の定義
const arrowVariable: (hikisu1: number, hikisu2: number) => number = (hikisu1, hikisu2) => hikisu1 + hikisu2;
変数arrowVariable
に1. 関数型の定義
で定義した型と一致するアロー関数を代入する。
arrowVariable
は、number
型の2つの引数hikisu1
とhikisu2
を受け取り、その合計をnumber
型として返す関数になる。
このコードを読みやすくするには、型エイリアスを使用するとよい。
type SampleArrowFunction = (hikisu1: number, hikisu2: number) => number;
const sampleAddNumbers: SampleArrowFunction = (a, b) => a + b;
const sampleKakezanNumbers: SampleArrowFunction = (a, b) => a * b;
const sampleDivisionNumbers: SampleArrowFunction = (c, d) => c / d;
console.log(sampleAddNumbers(1, 3)); //--> 4
console.log(sampleKakezanNumbers(3, 3)); //--> 9
console.log(sampleDivisionNumbers(sampleKakezanNumbers(3, 3), 3)) // --> 3
void型
戻り値が存在しないことを示すために使用される型。
function voidFunctionSample(): void {
console.log('戻り値なし!');
}
voidFunctionSample(); //--> 戻り値なし!
void
型を指定した関数でreturn
しようとするとエラー。
function voidFunctionWithReturn(): void {
console.log('戻り値なし!');
return 'これはエラーの関数'
}
void
を指定した関数の変数に、値を返却する関数を代入すると戻り値の型情報は無視される。
戻り値voidの関数型
type VoidFunctionType = () => void;
const returnStringFunction: VoidFunctionType = () => {
return "文字列!"
}
const okResult = returnStringFunction(); //void型
okResult
をstring型のように扱おうとするとエラーになる
console.log(okResult.toUpperCase());
関数のオーバーロード
二つの引数を合算して返却する関数addNumbers
。
function addNumbers(a, b) {
return a + b;
}
この関数にそれぞれ引数を渡して実行すると・・・
引数に渡す値によって結果が異なる。
- 数値→加算した結果を返却
- 文字列→引数を連結して返却
- 数値&文字列→文字列の連結(暗黙的型変換により、数値が文字列になる)
上記のような関数あった場合、number | strint
として型を設定しtypeof
演算子を使って型チェックをするのでも良いが、TypeScriptによる型推論でエラーとなる。
このエラーの原因としては、「戻り値がnumber
型の可能性があり、string
型専用のメソッドをエラーなく呼び出せることができない」とTypeScriptが判断したから。
ここで、同じ関数名に対してどのような関数なのかを表現する型定義をすることができる(呼び出しシグネチャ)
function addNumbers(a: number, b: number): number;
function addNumbers(a: string, b: string): string;
function addNumbers(a: number, b: string): string;
function addNumbers(a: string, b: number): string;
異なるパラメータと戻り値を持つ関数オーバーロードシグネチャを複数宣言することによって、TypeScriptは「addNumbers
は複数の呼び出し方がある!」と判断する。
let resultAdd_5 = addNumbers("1", "2");//関数の呼び出し
console.log(resultAdd_5.includes("1"));//結果:true