【TypeScript】基礎的な型のつけ方 - 型の種類を整理する
今回はTypeScriptの基本的な型の使い方について説明します。
説明する環境は以下です。
- macOS Catalina v10.15.5
- Visual Studio Code v1.57.0
- TypeScript v4.3.5
- ESLintでエラーチェック
TypeScriptの基本的な型のつけ方
JavaScriptはどんな型であっても変数に代入することができます。例えばnumber型が入っている変数にbooleanを代入することも可能です。ただ、これだと開発する上でエラーの原因になるため、TypeScriptを使って型を明確に指定する必要があります。
基本的な型のつけ方
変数の横にコロンをつけて型を指定します。1度変数に型を指定したら、それ以外の型は入れることができなくなります。
//boolean型
let hoge:boolean = true;
//number型
let aho:number = 100;
//string型
let boke:string = "こんにちは";
/*-- NG --*/
hoge: "どうも"; //boolean型にstring型は代入できない!
型を書かなくても型を推測してくれる - 型推論
自分で型を指定しなくとも、代入する値によって型を推測してくれます(オブジェクトでも配列でも)。すべてに対して型を推測してくれるわけではないので、状況に応じて型を指定しましょう。
//boolean型
let hoge = true; //trueが入っているので、hogeはboolean型と推測してくれる
//number型
let aho = 100; //100が入っているので、ahoはnumber型と推測してくれる
//string型
let boke = "こんにちは"; //"こんにちは"が入っているので、string型と推測してくれる
オブジェクトに型をつける
オブジェクトに対して型を指定する場合は以下。
/*--- 型をつける前 ---*/
const hoge = {
name: "太朗",
age: 30
}
/*--- 型を指定 ---*/
const hoge:{
name:string; //型
age: number; //型
} = {
name: "太朗",
age: 30
}
配列に型をつける
配列の場合は3つの例を作りました。
- 例1 … すべてstringの配列
- 例2 … stringかnumberかbooleanのいずれかがある配列(Union型)
- 例3 … string、number、bookeanの順番通り、3つのみ入る配列(Tuple型)
/*--- 例1 ---*/
//型をつける前
const hoge = ['佐藤','山田','鈴木'];
/*--- 例2 ---*/
//型をつける前
const boke = ['佐藤', '山田', 10, true];
/*--- 例3 ---*/
//型をつける前
const ahoaho = ['山田', 10, true];
/*--- 例1 ---*/
//型を指定する - すべてstring型の場合
const hoge:string[] = ['佐藤','山田','鈴木'];
/*--- 例2 ---*/
//型を指定する - stringかnumberかbooleanのいずれか
const boke:( string|number|boolean )[] = ['佐藤', '山田', 10, true];
/*--- 例3 ---*/
//型を指定する - 必ずこの順番通りで、3つだけ型が必要な場合
const ahoaho:[ string, number, boolean ] = ['山田', 10, true];
Enumでまとまったグループの型を指定する
Enumを使うと型を1つのブロックにまとめて管理できます。Enumを使った例と使わなかった例を使って説明します。
以下はEnumを使わなかった例です。
/*--- Enumを使わない例 ---*/
const CarsColor = {
RED: 'red',
BLUE: 'blue',
GREEN: 'green',
YELLOW: 'yellow',
};
const cars = {
suv: true,
color: CarsColor.RED,
};
//上のように指定の方法だと、stringの型を持つcolorは後でも変更できてしまう
cars.color = 'pink'; //これができてしまう
変数carsのcolorは、CarsColorのオブジェクトの中から選択できるようにしています。ただし、これだとcolorの型はstringになるため、CarsColorに定義されたものじゃなくても変更できてしまいます。(15行目)
そういった場合にEnumでまとめて型を定義しておくと、Enumで定義された型以外は選択できなくなります。(14行目)
/*--- Enumを使った例 ---*/
enum CarsColor {
RED = 'red',
BLUE = 'blue',
GREEN = 'green',
YELLOW = 'yellow',
};
const cars = {
suv: true,
color: CarsColor.RED,
};
cars.color = 'pink'; //これはエラー。colorに値にpinkを代入しようとしても、Enumにない項目はダメ。
cars.color = CarsColor.GREEN; //この指定はOK!enumで指定した4つの中からしか選択できなくする
何でも入るany型
any型とはいままでのJavaScriptと同じように変数に何でも入れることができます。これを使ってしまってはTypeScriptを使う意味がないので、極力使わないようにします。
let ahoaho: any;
ahoaho = 'テスト'; //stringでも
ahoaho = true; //booleanでも
ahoaho = ['りんご', 'ゴリラ', 'ラッパ']; //配列でも
ahoaho = { 果物: 'りんご' }; //オブジェクトでも
//anyが絡んだものはTypeScriptは型を見なくなる!
複数の型を指定するUnion型
AもしくはBのように指定できるのがUnion型です。
let hoge: string | number; //string型もしくはnumber型が入る
hoge = 'ほげ'; // stringだからOK
hoge = 100; //numberだからOK
hoge = true; //booleanは型定義していないのでエラー!
/*-- 配列で使う場合は以下 --*/
let aho:(string | number)[]; //string型もしくはnumber型の配列
aho = ['こんにちは', 1000];
特定の値のみ扱えるLiteral型
特定の値を指定すればそれ以外を受付けなくなります。constで変数を指定した場合は再代入ができないことから必然的にLiteral型になります。
let tomato: 'tomato'; //'tomato'というLiteral型
tomato = 'tomato'; //tomatoしか扱えなくなる
let num:111; // 111というLiteral型
num = 111; // 111しか扱えなくなる
//ちなみにconstはLiteral型になる
const apple = "りんご"; //りんごという型
使い所としてはUnion型とLiteral型を合体させて使えます。
let fruits: 'りんご' | 'バナナ' | 'みかん';
fruits = 'りんご'; //OK
fruits = 'みかん'; //OK
fruits = 'パイン'; //型の中にないのでエラー!
Enumのようにオブジェクトのような指定の仕方ができないので、型を指定する数が2つなど少ない場合にはLiteral型が使えます。
使い方は以下です。enumとの違いを整理しておきましょう。
const cars: {
suv: boolean;
color: 'red' | 'blue' | 'green' | 'yellow';
} = {
suv: true,
color: 'green',
};
型を変数のように使うtypeエイリアス
typeエイリアスでは、型を変数のように使い回すことができます。typeの後に好きな名前をつけて型指定すれば、その名前で型を使い回せます。
/*--- typeエイリアスを使わない例 ---*/
const cars: {
suv: boolean;
color: 'red' | 'blue' | 'green' | 'yellow';
} = {
suv: true,
color: 'green',
};
/*--- typeエイリアスを使った例 ---*/
type CarsColor = 'red' | 'blue' | 'green' | 'yellow';
const cars: {
suv: boolean;
color: CarsColor;
} = {
suv: true,
color: 'green',
};
typeエイリアスを拡張する
typeエイリアスを拡張したい場合は&を使います。
typeエイリアスのGradesにtypeエイリアスのChildを&でつなげています。その結果、型Gradesを指定された変数childrenは以下の4つのプロパティを持つ必要があります。
- name
- age
- English
- Math
type Child = {
name: string;
age: number;
};
type Grades = Child & { //typeエイリアスを拡張!
English: number;
Math: number;
};
const children: Grades = { //4つのプロパティが必要
name: "山田",
age: 15,
English: 100,
Math: 90
};
違う書き方としてはもう1つtypeエイリアスを用意して、そこにChildとGradesを&で繋げてもOKです。
type Child = {
name: string;
age: number;
};
type Grades = {
English: number;
Math: number;
};
type Children = Child & Grades; //ここで拡張!
const children: Children = {
name: "山田",
age: 15,
English: 100,
Math: 90
};
typeエイリアスとinterfaceの組み合わせでも&で拡張できます。
interface Child {
name: string;
age: number;
};
type Grades = {
English: number;
Math: number;
};
type Children = Child & Grades; //typeエイリアスにinterfaceを拡張!
const children: Children = {
name: "山田",
age: 15,
English: 100,
Math: 90
};
どちらか一方のtypeエイリアスだけ満たせていればOKな場合はUnion型で指定できます。
type Child = {
name: string;
age: number;
};
type Grades = {
English: number;
Math: number;
};
type Children = Child | Grades; //どちらか一方のtypeエイリアスをすべて満たせていればOK
const children: Children = {
name: "山田",
age: 15,
//English: 100, 無くても問題ない
Math: 90
};
オブジェクトやclassにのみ定義できるinterface
typeエイリアスとinterfaceの使い方はほとんど一緒ですが、interfaceはオブジェクトやclassのみに使用できます。もちろん関数にも使えます。
また、書き方の違いとしてはinterfaceには=が無く、typeエイリアスには=があります。
/*-- interfaceの例 --*/
interface Vegetables { // = が無い
パプリカ: string;
小松菜: string;
なすび: string;
トマト: string;
};
/*-- typeエイリアスの例 --*/
type Vegetable = { // = がある
パプリカ: string;
小松菜: string;
なすび: string;
トマト: string;
};
typeエイリアスは、オブジェクトの型以外にUnion型と合わせることができますが、interfaceではできません。
interface Vegetables {
パプリカ: string;
小松菜: string;
なすび: string;
トマト: string;
}; //Union型と合わせて指定できない
type Vegetable = {
パプリカ: string;
小松菜: string;
なすび: string;
トマト: string;
} | number; //Union型と合わせることができる
interfaceを拡張する
interfaceは同じ名前のものがあれば、型が追加されていきます。
typeエイリアスの場合は同じ名前で指定するとエラーになるので注意。
interface Human {
name: string;
age: number;
}
interface Human { //同じ名前のinterface
gender: string;
}
const human: Human = { // 1つ目のHumanに2つ目のHumanの型が追加された
name: "山田",
age: 30,
gender: "男"
};
interfaceに他のinterfaceを拡張させたい場合はextendsを使います。
interface Human {
name: string;
age: number;
}
interface Gender extends Human { //拡張!
gender: string;
}
const human: Gender = {
name: "山田",
age: 30,
gender: "男"
};
interface同士だけではなく、interfaceとtypeエイリアスでも拡張できます。
type Human ={
name: string;
age: number;
}
interface Gender extends Human { //interfaceにtypeエイリアスを拡張!
gender: string;
}
const human: Gender = {
name: "山田",
age: 30,
gender: "男"
};
typeエイリアスとinterfaceの違い、使いどころについては以下の記事を参考になります。
関数に型をつける
パラメーターに何も型を指定しないとany型になってしまうので、必ず型を指定します。関数が返す値への型は基本的には型推論されますが、状況に応じて型を書きましょう。
/*--- 型を指定していない例 ---*/
const hoge = (num1, num2) => {
return num1 + num2;
};
hoge(10, 20);
/*--- 型を指定している例 ---*/
const hoge = (num1: number, num2: number): number => {
return num1 + num2;
};
hoge(10, 20);
変数に関数を入れるときに、その変数にも型をつける場合は、関数自体の型の書き方が変わります。(2行目が:numberになっているのに対して、7行目は =>numberになっている)
//関数とパラメーターに型をつける場合
const hoge = (num1: number, num2: number): number => {
return num1 + num2;
};
//変数に関数を入れたとき、関数自体の型の書き方が変わる
const hogehoge: (num1: number, num2: number) => number = hoge;
関数に戻り値がない場合はvoidを使う
関数に何も返す値がない場合はvoidを指定します。
const ahoaho = (num: number):void => {
console.log(num);
};
ahoaho(100);
コールバック関数に型を指定する
コールバック関数に型を指定する場合はパラメーターに型、それからコールバック関数が返す値の型を指定します。(9行目)
const myName = (name: string) => {
console.log(`私の名前は${name}です`);
};
const myAge = (age: number) => {
console.log(`年齢は${age}歳です`);
};
const intro = (n: string, a: number, callback: (n: string) => void, callback2: (a: number) => void) => {
console.log('自己紹介をさせてもらいます。');
callback(n);
callback2(a);
};
intro('田中', 90, myName, myAge);
さいごに
今回はTypeScriptで使用する基本的な型のつけ方について説明しました。すべての要素に型を書くと思うと大変ですが、基本的には型推論で型を判断してくれるので、必要に応じて型を指定するイメージです。それでも複雑な処理が多くなってくると型を指定する機会は増えていきます。
TypeScriptの環境設定については以下の記事に書きました。Webpack5を使う場合は参考にしてみてください。
TypeScriptの応用的な使い方は以下の記事をどうぞ。