こんにちは、エンジニアのザワです。
私がTypeScriptを使い始めて季節が一巡と少し経ちました。いつの間にかTypeScriptに毎日触れる生活になりました。今回はこれまでの短いTypeScript人生の中で便利だと心の底から思ったことを整理していきたいと思います。
パッと思いつくのは下記の3つです。
- 関数が何を受け取るのかわかりやすい
- 何のIDかわかりやすい
- APIリクエスト・レスポンスがわかりやすい
3つを通して思うのは、探したり考えたりするいわゆる「作業的な時間」が減るので開発中のストレスを感じることも減ったということです。
では、前述した3つを詳しく説明していきたいと思います。
1.関数が何を受け取るのかわかりやすい
const Position = {
Top: 1,
Right: 2,
Bottom: 3,
Left: 4,
};
const getPosition = (value) => {
let position = '';
switch (value) {
case Position.Top:
position = 'TOP';
break;
case Position.Right:
position = 'RIGHT';
break;
case Position.Bottom:
position = 'BOTTOM';
break;
case Position.Left:
position = 'LEFT';
break;
}
return position;
};
console.log(getPosition(Position.Bottom)); // BOTTOM
上記のコードは、位置を取得する処理です。getPosition関数に着目してみましょう。引数として何らかを受け取り、その何らかによって、返す文字列を出し分けています。Positionオブジェクトが同じファイルに隣接しているので引数のvalueは数値が入ってくるんだなと推測できますが、Positionを別のファイルに定義していたらどうでしょうか。
const getPosition = (value) => {
let position = '';
switch (value) {
case Position.Top:
position = 'TOP';
break;
case Position.Right:
position = 'RIGHT';
break;
case Position.Bottom:
position = 'BOTTOM';
break;
case Position.Left:
position = 'LEFT';
break;
}
return position;
};
console.log(getPosition(Position.Bottom)); // BOTTOM
引数valueは何が入ってくるのか予想が難しくなります。次に引数に型を定義してみます。
const getPosition = (value: number) => {
let position = '';
switch (value) {
case Position.Top:
position = 'TOP';
break;
case Position.Right:
position = 'RIGHT';
break;
case Position.Bottom:
position = 'BOTTOM';
break;
case Position.Left:
position = 'LEFT';
break;
}
return position;
};
console.log(getPosition(Position.Bottom)); // BOTTOM
valueが数値で入ってくることがわかったので大分読みやすくなりました。もう少し改良してみたいと思います。別ファイルにPositionという型を定義してみます。
export const Position = {
Top: 1,
Right: 2,
Bottom: 3,
Left: 4,
} as const;
export type Position = typeof Position[keyof typeof Position];
getPositionの引数valueにPositionという型を指定しました。
const getPosition = (value: Position) => {
let position = '';
switch (value) {
case Position.Top:
position = 'TOP';
break;
case Position.Right:
position = 'RIGHT';
break;
case Position.Bottom:
position = 'BOTTOM';
break;
case Position.Left:
position = 'LEFT';
break;
}
return position;
};
console.log(getPosition(Position.Bottom)); // BOTTOM
Positionにカーソルをホバーすると、value引数は数値1,2,3,4のいずれかを受け取ることがわかります。
先ほどは数値を受け取ることまではわかったのですが、今回は選択肢を絞ることで何を受け取るかが明確になりました。
2.何のIDかわかりやすい
関数の引数を例にとって見ていきます。受け取ったIDをコンソールに出力する単純な関数です。
const hoge = (id: number) => {
console.log(id);
};
hoge(1);
引数にIDを受け取ることはわかりますが、何のIDか引数からはわかりません。Webアプリケーションを開発していると、ユーザIDや記事IDなど色々なIDが登場すると思います。
また、IDによっては数値の場合もあれば、文字列の場合もあります。この関数を見たときにそういった小さな悩みで頭を使いたくないので、何のIDか定義しておきます。
type UserId = number;
使う場合はこうなります。
const hoge = (id: UserId) => {
console.log(id);
};
hoge(1);
IDがユーザIDであることもわかるし、数値型であることもわかります。
最高です。
3.APIリクエスト・レスポンスがわかりやすい
これはどういうことかというと、タイトルのままですが、APIへのリクエスト・レスポンスそれぞれに型を定義することで、このAPIでは何をリクエストすればよいのか、どういうレスポンスが返ってくるのかが理解しやすくなるということです。
コード例を挙げて説明していきます。下記のコードは、記事を作成する処理です。処理自体はシンプルで、createPost関数で記事を作成したレスポンスをコンソールに出力しています。まずは型を定義していないパターンを見てみます。
import axios from 'axios';
const createPost = async () => {
const requestBody = {
title: 'foo',
body: 'bar',
userId: 1,
};
return await axios
.post('https://example.com/posts', requestBody)
.then((result) => result.data);
};
(async () => {
const result = await createPost();
console.log(result); // {title: "foo", body: "bar", userId: 1, id: 101}
})();
何をリクエストしているかはrequestBody変数の内容を見ればわかりますが、もしかすると他にもリクエスト可能なパラメータが存在していて、ここでは省略しているかもしれません。が、このコードだけでは事実はわかりません。
APIを返しているサーバサイドのコードを見る必要があります。レスポンスはどうでしょう? コンソールに出力するまで何が返ってくるのかわかりません。このコードを書いた2週間後には自分でも把握していないと思います。他の人が初見で改修する場合、さらに把握するのに時間が掛かると思います。
次にリクエスト・レスポンスを定義したコードを見てみましょう。
import axios from 'axios';
type PostId = number;
type UserId = number;
interface CreatePostRequest {
title: string;
body: string;
userId: UserId;
}
interface CreatePostResponse {
body: string;
id: PostId;
title: string;
userId: UserId;
}
const createPost = async () => {
const requestBody: CreatePostRequest = {
title: 'foo',
body: 'bar',
userId: 1,
};
return await axios
.post<CreatePostResponse>(
'https://example.com/posts',
requestBody
)
.then((result) => result.data);
};
(async () => {
const result: CreatePostResponse = await createPost();
console.log(result); // {title: "foo", body: "bar", userId: 1, id: 101}
})();
CreatePostRequestでリクエストをCreatePostResponseでレスポンスを型定義しています。どんなリクエストでどんなレスポンスが返ってくるのか即理解できます。処理自体を理解することに集中できるのでコードリーディングが捗ると思います。
リクエスト・レスポンスの型定義は別ファイルで定義することも考えられます。そんなときもエディタの推測やコードジャンプですぐ確認できます。
終わりに
今回は夕焼けを見つめながら思いついた、TypeScriptへの想いを3つのみご紹介しました。きっと他にも恩恵を受けていることはたくさんあるのでしょうけど、それについてはまた、ふきのとうが顔を出し始めた頃に心が躍ったら書こうかなと思います。
時間は有限です。残業もしたくありません。自分の脳みそはちっぽけなのでなるべく効率的に考えないといけないことに集中して開発したいです。なのでこれからもきっとTypeScriptと私は付き合っていくと思います。
それでは良い開発ライフを!
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。