TypeScript | 関数・クラス・ジェネリクス:関数設計の深化 – 戻り値型を省略すべき場面

TypeScript
スポンサーリンク

前提:「全部書く」か「全部省略」か、の二択ではない

まず大事なのは、
戻り値型は「常に書くべき」でも「常に省略すべき」でもない、ということです。

すでに話してきたように、
外部公開の関数・複雑な処理・ジェネリクスなどは「明示した方がいい場面」でした。

ここでは逆に、
「ここは素直に型推論に任せた方が、読みやすくて健全だよ」という場面を整理します。

キーワードは、
短い・局所的・文脈がはっきりしている、です。


パターン1:その場限りの小さなアロー関数(特にコールバック)

map / filter / forEach などの中の一行関数

一番典型的なのが、配列メソッドのコールバックです。

const numbers = [1, 2, 3];

const doubled = numbers.map((n) => n * 2);
TypeScript

ここで戻り値型をわざわざ書くと、こうなります。

const doubled = numbers.map((n): number => n * 2);
TypeScript

もちろん間違いではありませんが、
読みやすさという意味では、ほとんど情報量が増えていません。

なぜかというと、

numbers が number[] なので、n は number と推論される
n * 2 は number なので、戻り値も number と分かる
map の戻り値は number[] と型定義に書いてある

という「文脈」がすでに十分だからです。

このように、

配列の map / filter / reduce などの中で使う
1〜数行程度の小さなアロー関数
引数の型が文脈から明らかで、戻り値も単純な式

という条件がそろっているときは、
戻り値型は省略してしまった方が、コードがスッキリします。

「読めば一瞬で分かる型」を、わざわざ文字として重ねない、という感覚です。


パターン2:ローカル関数で、処理も型も単純なとき

ファイルの中だけで完結する、小さな補助関数

例えば、ある関数の中でだけ使う「ちょっとした補助関数」。

function main() {
  function toUpper(value: string) {
    return value.toUpperCase();
  }

  console.log(toUpper("hello"));
}
TypeScript

ここで toUpper の戻り値型は、
string と書いてもいいですが、省略してもほぼ困りません。

理由はシンプルで、

引数が string
value.toUpperCase() の戻り値も string
toUpper 自体が main の中だけで使われている

という状況だからです。

「この関数は外から呼ばれない」「このスコープの中だけで完結している」
かつ「処理も型も単純」なローカル関数は、
戻り値型を省略しても、設計上のリスクはほとんどありません。

むしろ、
「ローカルな細かいところまで型注釈だらけ」になると、
本当に見たい“重要な型”が埋もれてしまいます。

局所的で単純な関数は、
推論に任せてコードを軽くしておく、というバランス感覚が大事です。


パターン3:テストコード・サンプルコード・一時的なスクリプト

「仕様を固定する」必要がないコード

例えば、テストコードの中でだけ使う関数。

function createUser(name: string) {
  return {
    id: 1,
    name,
  };
}
TypeScript

ここで戻り値型を厳密に定義してもいいですが、
テストの文脈では「このファイルの中でしか使わない」「仕様として公開しない」ことが多いです。

サンプルコードや、
一時的に動かしてみるスクリプトも同じです。

こういうコードは、

長期的に保守する前提ではない
外部から再利用される前提でもない

ので、
戻り値型を省略して「とにかく挙動を確認する」ことを優先しても構いません。

もちろん、
テストコードでも型をきっちり書くスタイルもありますが、
初心者のうちは「本番コードの設計」に意識を集中させて、
テストやサンプルでは推論に甘える、という割り切りもアリです。


パターン4:ジェネリクスを使わない、単純な関数宣言

中身を見れば一瞬で分かる戻り値型

例えば、こんな関数。

function add(a: number, b: number) {
  return a + b;
}
TypeScript

戻り値型は number です。
書くとこうなります。

function add(a: number, b: number): number {
  return a + b;
}
TypeScript

どちらでもいいのですが、
「引数の型が単純」「戻り値も単純」「外部公開の“顔”でもない」
という条件なら、省略しても大きな問題にはなりません。

特に、
小さなアプリや学習用コードでは、
こういうレベルの関数にいちいち戻り値型を書くよりも、
「どんな関数をどう分割するか」という設計そのものに意識を割いた方が、成長につながります。

もちろん、
「外部に export する」「ライブラリ的に使う」関数になった瞬間に、
戻り値型を明示する側に切り替える、という線引きができていれば十分です。


パターン5:アロー関数で「引数型だけ明示」している場合

「引数は自分で決める、戻り値は推論に任せる」という分担

例えば、文脈のないアロー関数。

const double = (x: number) => x * 2;
TypeScript

ここで戻り値型は number ですが、
わざわざ : number と書かなくても、
x * 2 から自動で推論されます。

この「引数だけ型を書く」スタイルは、
アロー関数と相性がとても良いです。

引数の型は、自分の意図をはっきりさせるために明示する
戻り値の型は、式から推論させてコードを軽くする

という役割分担ができるからです。

特に、
「引数が1〜2個」「戻り値が単純な式」というアロー関数では、
このスタイルが一番読みやすくなります。

戻り値型を省略しても、
関数全体の型は (x: number) => number としてちゃんと推論されるので、
型安全性が落ちるわけではありません。


逆に「省略しない方がいい」境界線を意識しておく

ここまで「省略していい場面」を見てきましたが、
実は判断の軸はシンプルです。

戻り値型を省略しても、
その関数の「役割」「危険性」「仕様」が、
呼び出し側から見て十分に伝わるか?

という問いです。

その場限り・局所的・単純・文脈が強い
→ 省略しても読みやすさが勝つ

外部公開・複雑・失敗を含む・ジェネリクスを使う
→ 明示しておいた方が設計意図が守られる

この線引きさえ頭に置いておけば、
「全部書くべきか?」「全部省略していいのか?」で迷わなくなります。


まとめ:「戻り値型を省略すべき場面」を自分の言葉で言うと

最後に、あなた自身の言葉でこう整理してみてください。

戻り値型を省略していいのは、

その関数がローカルで、小さくて、処理も型も一目で分かるとき
配列の map / filter などのコールバックのように、文脈から型が明らかなとき
テスト・サンプル・一時スクリプトのように、「仕様として固定する必要」が薄いとき
アロー関数で、引数型だけ明示しておけば、戻り値は式から自明なとき

そして大事なのは、

「省略するかどうか」を、
めんどくさいからではなく、
“この関数の役割とリスクを考えたうえでの選択”として決めること。

コードを書きながら、
「この関数の戻り値、型として“見える化”しておいた方が、未来の自分は楽になるかな?」
と一度だけ自分に聞いてみてください。

その一呼吸が、
戻り値型を「書く/書かない」を使い分けられるエンジニアへの分かれ目になります。

タイトルとURLをコピーしました