「TypeScriptってよく聞くけどJavaScriptより難しそうで勉強するのが億劫に感じている」
「求人でよく見かけるTypeScriptって何のために使われているのかよくわからない」
「typeとinterfaceの違いがよくわからず勉強している」
今回はWeb制作やJavaScriptで開発をしている方に向けて、TypeScriptの基本を解説していこうと思います。
初めてTypeScriptを学ぶ方にとっては、型の書き方やその使い方に戸惑うこともあるかもしれません。
本記事ではTypeScriptを使ううえで最低限知っておきたい、型の書き方に絞ってついて説明します。
データに型を書くルールや、”type”や”interface”といった基本的な型の定義方法、”extends”や”extra”といった型の拡張方法、さらにはジェネリクス(T)の活用方法までを紹介します。
専門用語が登場しますが何かしらJavaScriptで開発をしたことがある方ならすぐにキャッチアップできる内容です。
また動画でも解説しているので必要に応じて活用してください。
TypeScriptにおける型の書き方と考え方
TypeScriptで型を指定して変数を宣言する
TypeScritpはあくまでJavaScriptの文法にデータの「型」を書くだけのものです。
「型」とはデータの種類のようなもので、文字、数字、配列などのことです。
例えば以下のように書きます。
// 文字列の変数を宣言している
let str: string = "hello";
// 数字の変数を宣言している
let num: number = 18;
名前の右側に「:」をつけて型名を書きます。
型名については文字列はstringで数字はnumberのように決まったキーワードがあるのですが、事前に暗記しておくというよりは実際に開発をやりながら1個ずつ覚えていくのが良いでしょう。
ちなみに型は「〇〇 | ▲▲」と書いて「〇〇もしくは▲▲の型」のように複数で書くこともできます。
// 文字列もしくは数字の変数を宣言して、初期値を文字列にした
let memberId: string | number = "0001";
// 後から数字を代入している
memberId = 12;
外部APIの取得など事前に型が不明な場合や、後から改修する見込みがある場合などに使うことがありますので覚えておきましょう。
TypeScriptで配列、オブジェクトに型を指定する
続いて配列、オブジェクトのように複数のデータを扱う場合の書き方です。
配列の場合だと以下のように書きます。
// 文字列の要素の配列を宣言している
let arr: string[] = ["aaa", "bbb", "ccc"];
このように複数のデータを扱う場合には中身の要素に対しての型も考えることになりますので注意してください。
また配列にも複数の型を指定することができます。
// 文字列もしくは数字の要素の配列を宣言している
let arr: (string | number)[] = ["aaa", "bbb", "ccc"];
さらに配列にはJavaScriptのメソッドが使えますが、メソッドに渡す引数の型が合わないとメソッドはエラーになります。
// 文字列の要素の配列を宣言している
let arr: string[] = ["aaa", "bbb", "ccc"];
// 数字を追加しようとする
arr.push(1);

初学者の方はエラーと聞くと怖い印象を持たれるかもしれませんが、逆に言うと「事前に間違いを教えてくれる」ということです。
TypeScriptを使う大きな動機がエラーを減らすことです。
そのためエラーになることは悪いことではなく、それだけミスを防いでくれていると考えてみましょう。
配列と同じようにオブジェクトについても同様の書き方と考え方になります。
オブジェクトにも中身のプロパティについても型を考えていきます。
// それぞれのプロパティの型を指定してオブジェクトを宣言している
let obj2: {
id: string;
name: string;
age: number;
};
オブジェクトで注意しないといけないのが、宣言したプロパティは過不足なく使用することです。
どう言うことかと言うと3つのプロパティを宣言したのに2つのプロパティしか使用しないと警告になります。
// それぞれのプロパティの型を指定してオブジェクトを宣言している
let obj2: {
id: string;
name: string;
age: number;
};
// ageを使わなかった
obj2 = {
id: "0001",
name: "aaa",
};

とはいえ実際の開発ではプロパティの必要性をすべて把握できるわけではありません。
その場合はJavaScriptのオプショナルチェーンを使用できます。
// それぞれのプロパティの型を指定してオブジェクトを宣言している
// ageは任意項目にしている
let obj2: {
id: string;
name: string;
age?: number;
};
// ageを使わなくても大丈夫
obj2 = {
id: "0001",
name: "aaa",
};
いわゆる必須か任意かを選ぶことができるわけですね。
「?」をつけることをJavaScriptで学んでこなかった方は一度以下の動画をご確認ください。
TypeScriptで関数の引数と戻り値に型を指定する
関数については引数と戻り値についての型を指定することになります。
以下は文字列の引数と数字の引数(任意)によって文字列の戻り値を出す関数になります。
引数もオブジェクト同様に任意項目にすることがあり、こちらもオプショナルチェーンを使用できます。
// 関数はデフォルトでは引数と戻り値に型を指定する
const func1 = (str: string, num?: number): string => {
console.log(str);
return "bbb";
};
func1("aaa");
関数の戻り値については処理内容によって不要な場合があります。
戻り値のない関数についてはvoidという型を戻り値に指定します。
// 関数の戻り値ではvoidという型があり、戻り値を不要とするもの
const func2 = (str: string): void => {
console.log(str);
};
func2("aaa");
TypeScriptで登場するtypeとinterfaceの使い方
TypeScriptではデータに型を指定するものだと説明してきました。
ここまでのお話が理解できれば開発に進んでも大丈夫なのですが、チーム開発では効率的に開発を行うためのルールがあります。
typeとinterfaceという型の書き方を説明していきますので、就職を考えている方はそれぞれマスターしておきましょう。
TypeScriptでtypeを使った型ルールを作る
まずtypeを使うことで型指定のルールを事前に作ることができます。
TypeScriptでは型を指定するわけですが、同じようなデータに何回もデータの指定をするのは実務では大変な作業になります。
そういった場合に繰り返し使いそうなデータには事前に型のルール(ひな型)を書くことで少々効率的な書き方ができるようになります。
例えば以下のような関数を書くとします。
const func4 = (name: string, num?: number) => {
console.log(name, num);
};
func4("aaa");
関数の引数は「文字列と数字(任意)」という型ですが、この組み合わせの引数は他の関数を作る時にも発生しそうな組み合わせです。
そのような場合に以下のように書くことができます。
type argType = {
name: string;
num?: number;
};
const func5 = (arg: argType) => {
console.log(arg.name, arg.num);
};
argTypeという名前で「文字列と数字(任意)」という組み合わせを事前に書くことで、同じ組み合わせの引数を使うときには「arg: argType」と書けばOKになります。
「この引数はパターン化できそうか?」という感覚は経験値がいるので初学者には難しく感じるかもしれませんがtypeの書き方は覚えておきましょう。
またtypeを使った関数を作った場合、その関数を実行するときは以下のように引数を指定します。
type argType = {
name: string;
num?: number;
};
const func5 = (arg: argType) => {
console.log(arg.name, arg.num);
};
// 関数の引数はオブジェクトで準備したため、関数の実行時の書き方の変更には初学者は注意
func5({ name: "aaa" });
通常の関数の引数の代入とは違う書き方ですよね、初学者の方は関数の実行までセットで書き方を覚えるようにしてください。
さらに引数だけではなく関数の作りまでパターン化することも可能です。
以下は「文字列と数字(任意)で戻り値はない関数」というものです。
引数と戻り値をパターン化した場合は関数名に「:」をつなげてtype名を書くので引数には型を書かなくて良くなります。
type initFunc = (first: string, second?: number) => void;
const func3: initFunc = (name, num) => {
console.log(name, num);
};
func3("aaa");
TypeScriptにおけるinterfaceの使い方
typeと同じようにパターン化する目的で使用するinterfaceというものもあります。
最近のトレンドとしてはinterfaceを使うことが多いのと、typeと若干書き方が違うのでしっかり練習しておきましょう。
// タイプと似たようなものでインターフェースもある、タイプと若干書き方が違うため初学者は注意
type argType = {
name: string;
num?: number;
};
interface ArgInterFace {
id: string;
name: string;
age: number;
}
interfaceにはtypeには無い特徴的な使い方があります。
まずはパターンの継承ができることです。
「パターンに追加で〇〇がある型」のような時に使うことができます。
interface ArgInterFace {
id: string;
name: string;
age: number;
}
// ArgInterFaceのパターンを引き継いだ新しいUserというパターンを作った
interface User extends ArgInterFace {
address: string;
}
// こちらはArgInterFaceを利用した
const test1: ArgInterFace = {
id: "0001",
name: "aaa",
age: 20,
};
// Userを型定義に利用する場合はaddressが無いとダメ
const test2: User = {
id: "0002",
name: "bbb",
age: 21,
address: "tokyo",
};
事前にArgInterFaceという3つのプロパティをパターン化したものを用意していましたが、急遽4つ目のプロパティを追加することになった例です。
もう1個新しいパターンを書き直しても良いですが、interfaceでは継承することで「ルールを書き足す」ことができます。
注意点として「上書き」では無いので、ArgInterFaceとUserは別物であることを認識しておく必要があります。
継承しても元のArgInterFaceは残り続けるわけです。
さらに継承の進んだ使い方でextraというものがあります。
継承だけでも便利ですが、ルールを書き足すことを繰り返すと何回も継承することになります。
例としてブログサイトを開発しているとします。
ブログ記事のデータをオブジェクトとして扱うことになり、id(ユニークID)とtitle(記事タイトル)とdesc(記事本文)の3つのプロパティを使うことになりました。
しかし後から記事ごとにカテゴリーとタグも追加したくなったときは継承ではなくextraというもので以下のような書き方ができます。
// 項目の有無が不明確だったり何回も継承する場合はextraが使える
interface Category {
category: string;
}
interface Tag {
tag: string;
}
interface ArgMore<T> {
id: string;
title: string;
desc: string;
extra: T[];
}
// 基本情報にカテゴリーを追加したいとき
const blog1: ArgMore<Category> = {
id: "0001",
title: "title1",
desc: "desc1",
extra: [{ category: "category1" }],
};
// 基本情報にタグを追加したいとき
const blog2: ArgMore<Tag> = {
id: "0001",
title: "title1",
desc: "desc1",
extra: [{ tag: "tag1" }],
};
基本のArgMoreというパターンにおいてextraというプロパティを書くことで、extraには任意で追加プロパティを追加することができるようになります。
上記の例ではblog1という記事にはカテゴリーを追加、blog2という記事にはタグを追加しています。
もちろん先ほどの継承でも同じことができますが、「何が来るかわからない」「何回も継承することになりそう」という場面ではextraの方が良いとされています。
またextra: T[ ];となっているようにextraは配列なので複数の追加もできるようになっています。
Tについては「ここには何か別のinterfaceの名前が入りますよ」という目印です。
blog3という記事にカテゴリーとタグを追加するには以下のようにします。
interface Category {
category: string;
}
interface Tag {
tag: string;
}
interface ArgMore<T> {
id: string;
title: string;
desc: string;
extra: T[];
}
// 基本情報にカテゴリーとタグを追加したいとき
const blog3: ArgMore<Category & Tag> = {
id: "0001",
title: "title1",
desc: "desc1",
extra: [{ category: "category1" },{ tag: "tag1" }],
};
typeやinterfaceなど専門用語が登場しますが、書き方はJavaScriptの基本であるオブジェクトや配列なので書いていくうちに慣れてくると思います。
今まで型を意識したことがないプログラムから1段上のレベルに行くためにTypeScriptも練習しておきましょう。