こんにちは!フロントエンドからNode.jsまで、日々のコードと向き合ってお疲れ様です。
TypeScriptを書き始めると、必ずと言っていいほどぶつかる疑問がありますよね。
「関数の戻り値の型って、自分で書くべき?それともTypeScriptに勝手に推論してもらうべき?」
ネットで調べると「推論に任せろ」と言われたり、別の記事では「いや、明示的に書け」と書いてあったりして、混乱していませんか?
結論からお伝えすると、どちらか一方が絶対的な正解というわけではありません。「コードの意図がどこにあるか」によって使い分けるのが、TypeScriptをスマートに乗りこなすプロの技なんです。
ここをクリアすれば、あなたのTypeScriptのコードはぐっと洗練され、エラーに怯えることがなくなりますよ。さあ、一緒にその境界線を探っていきましょう!
—
1. そもそも「型推論」と「明示的な型注釈」って何だっけ?
まずは基本のおさらいからいきましょう。TypeScriptの大きな魅力の一つが「型推論(Type Inference)」です。コンパイラが賢く気を利かせて、「あ、この書き方なら戻り値は数値だな」と自動で判断してくれる機能ですね。
型推論に任せるケース
// 戻り値の型を書いていないけれど、TypeScriptが「これは number だな」と推論してくれる
function add(a: number, b: number) {
return a + b; // number 型を返していることが自明
}
const result = add(1, 2); // result も当然 number 型になる
明示的な型注釈を書くケース
// 関数名の後ろに `: number` と、人間の手で型をハッキリ書く方法
function multiply(a: number, b: number): number {
return a b;
}
この2つの違い、パッと見は「書く手間が増えるかどうか」だけに見えますよね。でも、コンパイラの頭の中(型チェッカーの挙動)と、チームメンバーがコードを読むときの視点に立ってみると、まったく意味が変わってくるんです。
—
2. 【推論に任せるべきケース】シンプルさと変更のしやすさを優先する
基本的には、関数の内部が短く、処理の意図がひと目でわかる場合は、TypeScriptの型推論にどーんと任せてしまうのがスマートです。
なぜ推論が良いの?
余計な型を書かないことで、コードがすっきりして見通しが良くなります。また、将来的に返すデータ構造が少し変わったときも、型注釈を書き直す手間が減ります。
// ユーザーのフルネームを組み立てるだけのシンプルな関数
function createFullName(firstName: string, lastName: string) {
// 戻り値が string であることは誰の目にも明らか
return `${firstName} ${lastName}`;
}
ここで `string` とわざわざ書かなくても、TypeScriptは完璧に理解しています。DRY(Don’t Repeat Yourself:重複を避ける)の原則に従い、コンパイラの賢さを信頼してスッキリ書くのが、日々の開発では心地よいはずです。
—
3. 【明示的な型注釈が必須のケース】信頼性と「未来の自分・チーム」を守る
では逆に、どんなときに明示的な型注釈が必要になるのでしょうか?
ここは実務で一番差がつくポイントです。次の3つのシチュエーションを覚えておいてください。
① 公開APIや、他の人が使う共通関数(ライブラリ・ユーティリティ)
自分が書いた関数を、チームの他のメンバーや、未来の自分が使う場合です。
戻り値の型が明示されていないと、「この関数はいったい何を返す仕様なんだっけ?」と、わざわざ関数の中身を読みに行かなくてはなりません。
// ❌ 良くない例:中身を見ないと何を返すか分からない
function fetchUserData(userId: string) {
return httpGet(`/users/${userId}`);
}
// ⭕ 素晴らしい例:契約(インターフェース)がひと目でわかる
interface User {
id: string;
name: string;
}
function fetchUserData(userId: string): Promise
return httpGet(`/users/${userId}`);
}
明示的な型注釈は、他の開発者への「この関数は絶対にこの形のデータを返します」という契約書(ドキュメント)の役割を果たします。
② 複雑な条件分岐や、うっかりミスを防ぎたいとき
関数の行数が長かったり、途中で複雑な条件分岐がある場合、推論に頼っていると「意図しない型を返していた!」というバグに気づきにくくなります。
// 意図としては文字列を返したいのに、バグで数値が混ざってしまった例
function processStatus(statusId: number) {
if (statusId === 1) {
return “Active”;
} else if (statusId === 2) {
return “Pending”;
} else {
// うっかり数値の 0 を返してしまった!
return 0;
}
}
もしこの関数に `: string` という型注釈をあらかじめ貼っておけば、TypeScriptは即座にエラーを吐いて教えてくれます。
// ❌ コンパイルエラーになる!
// 「型 ‘number’ の値を型 ‘string’ の戻り値に割り当てることはできません」
function processStatus(statusId: number): string {
// …さっきの処理…
return 0; // TypeScriptがここで怒ってくれる!
}
このように、「自分のうっかりミスをコンパイラに監視してもらうための防壁」として、型注釈は絶大な効果を発揮します。
③ 再帰関数(自分自身を呼び出す関数)
再帰関数は、型推論のウイークポイントです。TypeScriptはコードを上から下に(あるいは文脈をたどって)解析するため、自分自身を呼び出す関数の戻り値をうまく推論できないことがあります。
// ❌ 戻り値の型を推論させようとすると、TypeScriptが迷子になってエラーになることがある
function countdown(n: number) {
if (n <= 0) return "Done";
return countdown(n - 1);
}
// ⭕ 戻り値を string と明示すれば、迷子にならずスッキリ解決!
function countdown(n: number): string {
if (n <= 0) return "Done";
return countdown(n - 1);
}
---
4. 陥りがちな罠:anyの感染に気をつけよう
型推論にまつわる、初学者が一番ハマりやすい罠についても触れておきます。それは「暗黙の `any`」です。
例えば、初期値を与えずに変数を宣言したり、型が不明なデータを扱うとき、TypeScriptが「うーん、型がわからないから、とりあえず最強(最凶)の `any` にしとこ」と諦めてしまうことがあります。
// 戻り値の型が不明なまま、any を返す関数になってしまう危険な例
function parseData(jsonString: string) {
return JSON.parse(jsonString); // 返り値は `any` 型になる
}
const data = parseData(“{}”);
data.foo.bar(); // コンパイルエラーにならないが、実行時エラーの爆弾になる!
`JSON.parse()` は `any` を返すため、それをそのままreturnしているこの関数も、実質的に「なんでも返す関数」になってしまいます。これではTypeScriptを使っている意味が半減してしまいますよね。
こういったケースでは、必ず戻り値の型注釈(または型アサーション)を明示し、安全性を担保しましょう。
interface AppConfig {
theme: string;
}
// 戻り値を明示することで、安全なコードに生まれ変わる
function parseConfig(jsonString: string): AppConfig {
return JSON.parse(jsonString);
}
—
5. まとめ:実践で迷ったときの判断基準
ここまで、関数の戻り値の型推論と明示的な型注釈のトレードオフを見てきました。最後に、明日からのコーディングで迷わないための判断基準をまとめますね。
- 基本は推論に任せる
- 関数の中身が短く、誰が見ても何を返すか分かる場合。
- プライベートなヘルパー関数など、ファイル内で完結する場合。
- 明示的な型注釈を書く
- 他の人が使う共通の関数や、外部に公開するAPI・コンポーネントの場合。
- 複雑な処理で、「絶対にこの型を返したい」という意図(仕様)をコードに刻み込みたい場合。
- 再帰関数を書く場合。
TypeScriptの型システムは、あなたを縛るための窮屈なルールではありません。「コードの意図を明確にし、未来のバグからあなたを守ってくれる頼もしい相棒」です。
この境界線がスッと腑に落ちれば、もうTypeScriptの基本はバッチリマスターできていますよ!自信を持って、日々の開発を楽しんでいきましょう。