はじめに
4月から新しい現場で初めてTypeScriptを書いている。
ざっくり概要はわかっているつもりだが、「なんかよくわからないけど型のエラーが出る」ということによく遭遇するので、自分の知識の整理のためにまとめた。
前提
TypeScriptの型は便利機能のうちの一つ。
TypeScriptで書かれたファイル(.ts
)は、JavaScriptのファイル(.js
)にコンパイルされ、実行時には.js
ファイルが実行される。
コンパイルされた.js
ファイルには型の情報は出力されない。
.js
ファイル(コンパイル後)
→型の情報は入ってない。
JavaScriptにも型という概念は存在するが、型を宣言する構文は存在しない。
型をうまく使うことでバグを防げたり、開発を効率よくできたりする。
正しく理解することが大切。
基本的な型の定義
型定義のやりかた
TypeScriptの型を定義するには変数名:型
とする。
const hoge: string = "hoge";
let piyo: number = 0;
piyo = "piyo"; // エラーになる
基本的な型として、上記のstring
、number
のほか、boolean
、null
、undefined
、any
がある。
JavaScriptでのデータ型とほぼ同じだが、any
はTypeScript独自の型。
どんな値でもいれることができる。
型推論
TypeScriptには型推論というものがあり、自動的に型を推測してくれる。
そのため、文字列や数値を変数にそのまま格納する分には、型宣言を書く必要がない。
基本的には型推論に任せるのがいいとされる。
型推論されている変数の例
→"hoge"
型になっている。
※string
型になっているのと同じ。
string
の中でも"hoge"
という固定の文字列の型になっている。
ただし、変数宣言時に初期化しない場合、型はany
型になってしまう。
any
型はどんな値でも受け入れてしまうので好ましくない。
→数値を入れても文字列を入れてもエラーにならない。
いろいろな型の定義
配列
配列は型名[]
で定義できる。
let arr: string[] = ["apple", "banana"];
arr.push("orange");
arr.push(100); //エラーになる
オブジェクト
オブジェクトは{プロパティ名:型名}
で定義できる。
let person: {
firstName: string;
age: number;
isValid: boolean;
} = {
firstName: "John",
age: 24,
isValid: true,
};
// パラメータの不足
person = { //エラーになる
age: 38,
isValid: false,
};
// 存在しないパラメータ
person = {
firstName: "Kevin",
age: 30,
isValid: true,
country: "America", //エラーになる
};
// プロパティの型違い
person = {
firstName: 0, //エラーになる
age: 24,
isValid: true,
};
person
のあとに:
(コロン)で続いているのがオブジェクトの型の定義、=
(イコール)の後に続いているのが設定したいオブジェクトの値となる。
オブジェクトの型を定義することで、プロパティの過不足のチェックや、プロパティに設定されている値の型チェックを行うことができる。
定義通りのオブジェクトの形式でないとエラーになる。
プロパティの不足
存在しないプロパティ
プロパティの型違い
関数型
関数も型を設定することができる。
function 関数名(変数名:型):戻り値の型{/* 関数の処理 */ }
なお、戻り値の型にはvoid
という特殊な型を設定できる。
これは戻り値を返さない場合に設定する。
function numToStr (num1:number):string {
return num1.toString();
}
numToStr(1);
// 引数が少ない
numToStr(); //エラーになる
// 引数が多い
numToStr(1,2); //エラーになる
// 引数の型誤り
numToStr('2'); //エラーになる
// 戻り値の型誤り
function hello():string {
return 100; //エラーになる
}
// 戻り値の型を設定しているのに返さない
function greeting():string { //エラーになる
console.log('hello');
}
// 戻り値を返さない場合の型
function sayHi():void {
console.log('Hi');
}
関数の型を定義することで、関数の呼び出しの際に引数の個数や型を指定することができる。
また、関数内で誤った型の値を返したり、値を返さなかったりすることを防げる。
型の定義にあった呼び出し方や、戻り値の定義をしていないとエラーになる。
引数が少ない
引数が多い
引数の型誤り
戻り値の型誤り
戻り値の型を設定しているのに返さない
アロー関数での型の定義
アロー関数も型の定義方法は同じ。
(引数:引数の型):戻り値の型
が関数の型定義時の形となる。
const numToStr = (num1:number):string => {
return num1.toString();
}
ユニオン型
|
を使うことで、複数の型を組み合わせて型を定義することができる。
let val : string | number;
val = 'hoge';
val = 100;
この場合、val
はstring
かnumber
のどちらかを満たしていればよい。
関数の引数が、文字列か数値のどっちかを受け取る、みたいなときに使える。
型エイリアス
型に名前をつけてオリジナルの型を作成できる。
ユニオン型はたいてい型エイリアスを使って定義する。オブジェクトも定義できる。
type エイリアス名 = 型定義
とする。
定義したエイリアスは、string
やnumber
などの型と同じように、変数の型として設定できる。
type NumOrStr = string | number;
type Person = { firstName: string; age: number; isValid: boolean };
const val: NumOrStr = "hoge";
const obj: Person = {
firstName: "John",
age: 28,
isValid: true,
};
NumOrStr
型と、Person
型を作成して、それぞれ変数の型として設定している。
エイリアスを設定することで再利用性が上がるし、適切なエイリアス名をつけることで用途が明確になる。
インターフェース
型の構造を定義することができる。
型エイリアスとにているが、インターフェースはオブジェクト型を定義するときに使う。
interface インターフェース名 {}
とする。
interface Person {
firstName: string;
age: number;
isValid: boolean;
}
const obj: Person = {
firstName: "John",
age: 28,
isValid: true,
};
インターフェースはextends
キーワードを使うことで、他で定義した構造を引き継いで新しい構造を作ることができる。
interface Person {
firstName: string;
age: number;
isValid: boolean;
}
interface BusinessPerson extends Person {
company: string;
}
const bizPerson: BusinessPerson = {
firstName: "Kevin",
age: 32,
isValid: false,
company: "ABC",
};
Person
をextends
してBusinessPerson
という新たなインターフェースを作成している。
BusinessPerson
のインターフェースにはfirstName
プロパティなどはないが、bizPerson
には設定できている。
これは、Person
の構造を引き継いでいるためである。
ベースとなるものに新しい要素を追加することで型の定義を拡張することができる。
まとめ
基本的な宣言方法から、型の種類についてざっと確認した。
最後にジェネリクスも紹介したかったが、これはまだ自身の理解が及んでいないので、もう少し理解できてから別枠で記事を書くことにする。
型をうまく使いこなせば、間違った方法で関数を呼ぶことを防げたり、オブジェクトの中身をある程度指定できたりするのは便利だと思った。
TypeScriptを使わない理由がないのでどんどん使い倒していきたい。