「戻り値型の省略」って、していいの?ダメなの?
まず結論から言うと、
TypeScript では 「戻り値型は省略しても動く」 し、「でも省略しない方がいい場面もたくさんある」 です。
function add(a: number, b: number) {
return a + b;
}
TypeScriptこの関数、戻り値型(): number)を書いていませんが、TypeScript は
「a + b は number だから、この関数は number を返す」と自動で推論してくれます。
だから、技術的には「戻り値型は必須ではない」。
でも、「いつ省略してよくて、いつ書いた方がいいのか」 を感覚として持っておくことが、実務ではかなり大事になります。
TypeScript がやってくれる「戻り値型の推論」
単純な式なら、ほぼ正確に推論される
たとえば、こんな関数たち。
function add(a: number, b: number) {
return a + b;
}
function greet(name: string) {
return `Hello, ${name}`;
}
function isAdult(age: number) {
return age >= 18;
}
TypeScriptTypeScript はそれぞれ、
addの戻り値型 →numbergreetの戻り値型 →stringisAdultの戻り値型 →boolean
と推論してくれます。
つまり、「return の中身がシンプルな式なら、戻り値型を省略してもほぼ困らない」 ということです。
呼び出し側では、ちゃんと型が効きます。
const n = add(1, 2); // n: number
const msg = greet("Taro"); // msg: string
const ok = isAdult(20); // ok: boolean
TypeScriptここまでは「省略してもいい世界」です。
省略したときに「危険」になりやすいパターン
途中で型が変わっても気づきにくくなる
例えば、こんな関数を考えます。
function getStatus(code: number) {
if (code === 200) {
return "ok";
} else if (code === 404) {
return "not_found";
} else {
return null;
}
}
TypeScript戻り値型を書いていないので、TypeScript は"ok" | "not_found" | null のような型を推論してくれます(かなり賢いです)。
でも、あとからうっかりこう変えてしまったとします。
function getStatus(code: number) {
if (code === 200) {
return "ok";
} else if (code === 404) {
return "not_found";
} else {
return "error"; // なんとなく null をやめた
}
}
TypeScriptこのとき、戻り値型は "ok" | "not_found" | "error" に変わります。
呼び出し側で「null を考慮していたコード」があっても、コンパイルエラーにはなりません。
もし最初からこう書いていたらどうでしょう。
function getStatus(code: number): "ok" | "not_found" | null {
// ...
}
TypeScriptこの状態で return "error" と書いたら、即エラーになります。
「戻り値型を明示しておくと、“関数の約束”を勝手に変えたときに TypeScript が怒ってくれる」
これが、戻り値型を省略しない大きなメリットです。
「省略していい場面」と「書いた方がいい場面」
省略してもいい場面(練習・小さな関数)
例えば、ファイルの中だけで完結している、小さなユーティリティ関数。
function double(n: number) {
return n * 2;
}
function toUpper(s: string) {
return s.toUpperCase();
}
TypeScriptこういう「単純で、すぐ上に実装が見える関数」は、戻り値型を省略してもそこまで困りません。
読み手も「中身を見ればすぐ分かる」し、推論もほぼ正確です。
学習中にサクサク書きたいときも、ここは無理に書かなくていいです。
「まずは関数を書くことに慣れる」 ほうが大事なフェーズもあります。
書いた方がいい場面(実務・外から呼ばれる関数)
逆に、次のような関数は、戻り値型を明示した方がいいです。
アプリのあちこちから呼ばれる「サービス関数」
API クライアント(サーバーからデータを取ってくる関数)
ライブラリとして公開する関数
戻り値がオブジェクト・配列・ユニオン型など、少し複雑なもの
例えば、API クライアント。
async function fetchUser(id: string) {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
return data;
}
TypeScriptこれだと、戻り値型は any に近い扱いになってしまいがちです。
実務では、こう書きたいです。
type User = {
id: string;
name: string;
};
async function fetchUser(id: string): Promise<User> {
const res = await fetch(`/api/users/${id}`);
const data = await res.json();
return data as User;
}
TypeScriptここで Promise<User> と書くことで、
「この関数を await したら、User 型が返ってくる」
「User にないプロパティを使おうとしたらエラーになる」
という世界が手に入ります。
「他のコードから見たときに、“この関数は何を返すのか”が一目で分かる」
これが、戻り値型を明示する一番の価値です。
戻り値型を省略したときの「落とし穴」になりがちな例
何も返さないつもりが、実は返してしまっている
function logAndCount(messages: string[]) {
let count = 0;
for (const m of messages) {
console.log(m);
count++;
}
return count;
}
TypeScriptこの関数、名前だけ見ると「ログを出すだけ」に見えますが、実は数も返しています。
戻り値型を書いていないと、「戻り値を使う前提なのか、そうでないのか」が曖昧になります。
意図が「ログを出すだけ」なら、こう書いた方がいいです。
function logMessages(messages: string[]): void {
for (const m of messages) {
console.log(m);
}
}
TypeScript逆に、「数を返すことが大事」なら、こう。
function logAndCount(messages: string[]): number {
let count = 0;
for (const m of messages) {
console.log(m);
count++;
}
return count;
}
TypeScript「この関数の“役割”は何か?」を戻り値型で表現する
その意識があると、省略するかどうかの判断も自然に決まってきます。
戻り値型を「書く練習」を少しだけしてみる
まずは「自分の頭の中のイメージ」をそのまま型にする
例えば、こんな関数を考えます。
function buildUser(id: number, name: string) {
return {
id,
name,
createdAt: new Date(),
};
}
TypeScript自分の頭の中では、きっとこう思っているはずです。
「id は number、name は string、createdAt は Date のオブジェクトを返している」
それをそのまま型にすると、こうです。
type User = {
id: number;
name: string;
createdAt: Date;
};
function buildUser(id: number, name: string): User {
return {
id,
name,
createdAt: new Date(),
};
}
TypeScriptこの「頭の中のイメージ → 型として書き下す」という練習をすると、
戻り値型を書くのがだんだん自然になってきます。
まとめ:戻り値型の省略は「サボり」ではなく「選択」
戻り値型を省略すること自体は、悪いことではありません。
TypeScript はかなり賢く推論してくれるので、特に小さな関数ではそれで十分なことも多いです。
でも、実務や少し大きめのコードになってくると、
「この関数は何を返すのか?」を明示しておくことが、設計の一部 になってきます。
だから、こんな感覚で使い分けるのがおすすめです。
練習中・小さな関数 → 省略してもOK。まずは書くことに慣れる。
他のコードからも呼ばれる関数 → 戻り値型を書いて、「約束」をはっきりさせる。
戻り値が複雑・重要 → 省略せず、むしろ積極的に型で表現する。
「この関数、何を返すつもりで書いてる?」
その問いに、自分でちゃんと答えられるようになったら、
戻り値型を書くか省略するかは、もうあなたの設計の選択です。
