TypeScriptのvoid型とは?戻り値なし関数の型注釈を初心者向けに解説
TypeScriptのコードを読んでいて、関数の戻り値の型に書かれたvoidを見て「これって何?」と引っかかった方は多いと思います。
voidって何の型?undefinedとどう違うの?
voidを書くべきか、書かなくてよいのか分からない…
() => voidになっているのに、値を返す関数を渡せるのが理解できない…
この記事では、TypeScriptのvoid型について、基本的な使い方からundefinedとの違い、コールバック関数での特殊な挙動、そしてnever型との違いまで、コード例とともに丁寧に解説します。型システム全体の中での位置付けも整理するので、voidの正体がすっきり理解できるはずです。
この記事は次のような方におすすめです。
- TypeScriptを学び始めて、関数の戻り値型でvoidを見かけて疑問を持った方
- voidとundefinedの違いをすっきり整理したい方
- コールバック関数のvoid型でつまずいている方
- void・undefined・neverをまとめて使い分けたい方
この記事を読み終わるころには、voidの正体と書き分けが分かり、型を意識した関数設計ができるようになります。
それでは、順を追って詳しく見ていきましょう!
- 未経験で後悔したくない
【実体験】未経験からITエンジニアに転職して後悔した話|4社経験してわかった「最初の選択ミス」 - 年収が低くて不安
4年間ずっと年収260万だったエンジニアが、転職で510万になるまでの全記録
TypeScriptのvoid型とは?まずは結論から
最初に結論をお伝えすると、TypeScriptのvoid型は 「値を返さない関数の戻り値」を表す特別な型 です。console.logのように「処理だけして何も返さない」関数の戻り値に付ける、と覚えておけばまずはOKです。
具体的には、次のように書きます。
function logMessage(message: string): void {
console.log(message);
// return文がない、または return; だけ
}
戻り値の型注釈にvoidを書くことで、「この関数は呼び出し元に値を渡さない」という意図を、型として明示できます。
なお、JavaScriptにはvoid 0のような 演算子としてのvoid もあり、混同されがちですが、本記事で扱うのは 型としてのvoid です。両者の違いはこのあとすぐ整理します。
void型の役割を一行で
void型を一言でまとめると 「この関数は値を返しません、と表明する型」 です。
JavaScriptには元々「戻り値なし」を表現する仕組みがなく、return文を書かない関数は暗黙的にundefinedを返します。TypeScriptでは型として「値を返さない」という意図を明示できるよう、専用のvoid型が用意されています。型注釈としてvoidを書いておくと、関数内でうっかり値をreturnしてしまったときにコンパイル時点でエラーになり、設計意図のズレに気づけるという実務的なメリットがあります。
「型としてのvoid」と「void演算子」は別物
ここで先回りして補足しておきたいのが、JavaScriptのvoid演算子と、TypeScriptのvoid型はまったくの別物だという点です。
// JavaScriptの void 演算子(式評価して undefined を返す)
const result = void 0; // result は undefined
// TypeScriptの void 型(戻り値の型として使う)
function doSomething(): void {
console.log("値は返しません");
}
void演算子はJavaScriptの式の一種で、どんな式の前に付けても結果をundefinedにする働きをします。一方、void型はTypeScriptが提供する 型 であり、関数の戻り値の型注釈に書くものです。検索結果ではこの2つが混ざって出てくることもありますが、本記事で扱うのは後者(型としてのvoid)です。
void演算子は、ブックマークレットなどでjavascript:void(0)のように使われることがあります。式の評価結果を捨ててundefinedにしたいときの構文ですが、現代のTypeScript開発で意識する場面はほぼありません。
void型の基本的な使い方
void型の書き方はシンプルです。関数の戻り値の位置にvoidと書くだけで、「この関数は値を返さない」と表明できます。ここでは、関数宣言・アロー関数・関数式それぞれの書き方と、型推論で自動的にvoidになるケースを順に見ていきます。
関数宣言・アロー関数での書き方
関数宣言・アロー関数・関数式の3パターンで、それぞれvoid型を書いてみます。
// 関数宣言
function logHello(): void {
console.log("Hello");
}
// アロー関数
const logHelloArrow = (): void => {
console.log("Hello");
};
// 関数式
const logHelloExpr = function (): void {
console.log("Hello");
};
ポイントは次の通りです。
- 戻り値の型注釈は 引数リストの閉じカッコの直後、
:に続けて書きます - アロー関数では
(): void => {}の順序になります - どの書き方でも意味は同じで、好みやプロジェクトの規約に合わせて使い分けます
引数や戻り値の型注釈の細かい書き分けについては、関数の引数と戻り値の型注釈の記事で詳しく扱っています。
型推論で自動的にvoidになるケース
実は、戻り値の型注釈を 省略しても 、TypeScriptは賢く戻り値の型を推論してくれます。return文がない関数や、return;しか書かれていない関数の戻り値型は、自動的にvoidに推論されます。
// 型注釈を省略した例
function logMessage(message: string) {
console.log(message);
}
// VS Code等でホバーすると: function logMessage(message: string): void
const greet = (name: string) => {
console.log(`Hello, ${name}`);
};
// 型: (name: string) => void
実務では、関数の意図を明示したい場合はvoidを明示的に書き、内部関数や短いユーティリティでは省略する、といった使い分けが多いです。「省略=悪」ではなく 、可読性と意図の明示のバランスで決めて構いません。
void型の関数でreturnを書くとどうなるか
戻り値型がvoidの関数では、returnの書き方にいくつかのルールがあります。
function ok1(): void {
// return を書かない → OK
}
function ok2(): void {
return; // 値なしの return → OK
}
function ng(): void {
return 42; // エラー: Type 'number' is not assignable to type 'void'
}
function ok3(): void {
return undefined; // OK(後述する void と undefined の関係による)
}
ポイントは次の通りです。
- 値を伴うreturnは原則NG(数値・文字列・オブジェクトなど何を返してもエラー)
- 値なしのreturn(
return;)はOK。早期returnしたい場面で使います return undefined;は許容される。これは「voidはundefinedの上位型」という型システム上の関係による特例です(次のセクションで解説)
ここで注意したいのは、 return文を書かないこととreturn;を書くことは、意味としては同じ という点です。早期に処理を抜けたい場面でreturn;を活用すると、コードの意図が読み取りやすくなります。
void型とundefined型の違い
void型を学ぶうえで一番つまずきやすいのが、undefined型との違いです。どちらも「値がない」雰囲気を持つ型なので、初学者には区別がつきにくいですが、 意図と使い場所がはっきり違います 。
一覧で整理する違い
まずは比較表で全体像をつかみましょう。
| 観点 | void | undefined |
|---|---|---|
| 意図 | 値を返さない関数の戻り値型 | 値undefinedそのものを表す型 |
| 主な使用場所 | 関数の戻り値型 | 変数・プロパティ・引数・戻り値全般 |
return;(値なし) |
OK | OK(TypeScript 5.1以降) |
return undefined; |
OK(特例) | OK |
| 値を伴うreturn | NG | NG(undefined以外は不可) |
| 型階層の関係 | undefinedの上位型 | voidの部分型 |
まずはこう覚えればOKです。 「戻り値を返さないつもりならvoid、明示的にundefinedを返すならundefined」 。実務ではvoidを使うことのほうが圧倒的に多いです。
voidはundefinedの上位型である
TypeScriptの型階層では、 voidはundefinedを含む上位型 という関係になっています。これにより、voidが期待される位置にundefinedを渡すことはできますが、その逆はできません。
function returnsVoid(): void {
return undefined; // OK: void は undefined を含む
}
function returnsUndefined(): undefined {
return undefined; // OK
// return; や return文の省略も OK(TypeScript 5.1以降)
}
// 代入関係
const v: void = undefined; // OK
const u: undefined = undefined; // OK
// const u2: undefined = undefined as void; // void → undefined への代入はエラー
ポイントは次の通りです。
- voidは「値を返さない」という意図の型なので、
undefinedという具体的な値が来てもエラーにしません - 逆に、 void型の値をundefined型の変数には代入できません (上のコード例の最後の行)
- TypeScript 5.1以降、戻り値型がundefinedの関数でも
return省略やreturn;が許容されるようになりました
代入の向きという観点で整理すると、void型のほうが 緩い 型、undefined型のほうが 厳しい 型と覚えておくと使い分けやすくなります。
型はvoid、実行時はundefinedというズレ
ここはTypeScript初学者がもっとも誤解しやすい部分です。 戻り値型がvoidの関数も、実行時には実際にはundefinedを返しています 。
function noReturn(): void {
console.log("値を返さない");
}
const result = noReturn();
console.log(result); // undefined
console.log(typeof result); // "undefined"
ポイントは次の通りです。
- 型情報は コンパイル時に消える ため、ランタイムには「voidという特別な値」は存在しません
- JavaScriptの仕様上、return文を書かない関数は暗黙的にundefinedを返します
- voidはあくまで 「呼び出し側に戻り値を使ってほしくない」という意図を型で表現する仕組み であり、実行時の値そのものを変える機能ではありません
この「型と実行時のズレ」は、TypeScript全般を理解するうえで重要な感覚です。型はあくまで開発時の安全装置であり、JavaScriptとして実行されたときには消えてしまう、という前提を押さえておきましょう。
コールバック関数でのvoidの特殊な挙動
void型でもう一つ理解しづらいのが、 コールバック関数の引数として() => voidを指定したとき、値を返す関数も渡せる という挙動です。一見すると型エラーになりそうですが、TypeScriptはあえてこれを許容しています。理由を含めて見ていきましょう。
値を返す関数もvoid型として扱える
まずは挙動を確認します。
type Callback = () => void;
const cb1: Callback = () => {
// OK: 何も返さない
};
const cb2: Callback = () => {
return 42; // OK: number を返しても代入できる
};
const cb3: Callback = () => "hello"; // OK: string を返しても代入できる
// ただし、Callback型として呼び出した戻り値は void 型なので、値として使えない
const result = cb2();
// const x = result + 1; // エラー
ポイントは次の通りです。
() => void型の変数には、 戻り値の型に関係なく 関数を代入できます- ただし、Callback型として呼び出した結果の 戻り値は使えません(型はvoid扱い)
- これは型エラーではなく、TypeScriptが意図的に許容している仕様です
なぜこの設計になっているのか
「呼び出し側が戻り値を使わない」ことを宣言しているのがvoidなので、 実際の関数が値を返していても呼び出し側には害がない 、というのがTypeScriptの設計思想です。実例を見るとイメージしやすくなります。
const numbers = [1, 2, 3];
// forEach のコールバックは (value, index, array) => void として定義されている
numbers.forEach((n) => n * 2); // OK: number を返す関数を渡せる
numbers.forEach((n) => console.log(n)); // OK: void を返す関数も渡せる
// もし「戻り値の型が一致しないとNG」だったら、push を直接渡せない
const result: number[] = [];
[1, 2, 3].forEach((n) => result.push(n));
// push は number を返すが、forEach は戻り値を使わないので問題ない
Array.prototype.forEachのコールバックは内部的に(value, index, array) => voidという型ですが、もしvoidの型一致を厳密にチェックしてしまうと、Array.prototype.push(numberを返す)を直接渡せなくなり、不便です。実務的には 戻り値を捨ててもよい 場面でvoidが使われており、そのままの形で関数を渡せるこの仕様はとても便利です。
実務でも、ボタンクリックハンドラやforEachのような「戻り値は使わない前提のコールバック」で頻繁に恩恵を受けます。「voidは無視のサイン」と覚えておくと腑に落ちやすいです。
void型とnever型の違い
void型と並んでよく混同されるのがnever型です。どちらも「値を返さない」雰囲気を持っていますが、 意味するところはまったく違います 。
voidは「値を返さない」、neverは「返ってこない」
両者の違いを端的に表すと次の通りです。
- void: 関数は正常終了して呼び出し元に制御が戻るが、値は返さない
- never: そもそも関数が正常終了しない(例外を投げる、無限ループする等)
コードで比較してみましょう。
// void: 正常に終了するが値を返さない
function logSomething(): void {
console.log("処理だけ実行");
// 関数が完了して呼び出し元に戻る
}
// never: 必ず例外を投げる(呼び出し元には戻らない)
function throwError(message: string): never {
throw new Error(message);
// この関数は呼び出し元に制御を返さない
}
// never: 無限ループも never
function infiniteLoop(): never {
while (true) {
// 永遠に終わらない
}
}
ポイントは次の通りです。
- voidは「呼び出し元に戻る」が前提 。値だけ返さない
- neverは「呼び出し元に戻らない」 。例外送出や無限ループの結果として、関数が完了しないことを表す
- 型階層上、neverはすべての型の部分型(最下位)、voidはundefinedの上位型なので、両者の立ち位置は型階層上もまったく異なります
「値はないけど戻ってくるならvoid、そもそも戻ってこないならnever」と覚えておけば、まず実務で困りません。
void型でやりがちなアンチパターンと注意点
最後に、void型を使うときに初学者がつまずきやすいパターンを3つ整理します。どれも実務で見かけるNGパターンなので、改善方法とセットで押さえておきましょう。
戻り値があるのにvoidと書いてしまう
意図せずvoidと書いてしまい、呼び出し側で戻り値が使えなくなるケースです。
// NG: 実は値を返したかったのに void と書いてしまう
function calculateTotal(items: number[]): void {
const total = items.reduce((sum, n) => sum + n, 0);
return total; // エラー: Type 'number' is not assignable to type 'void'
}
// OK: 戻り値型を正しく書く(または型注釈を省略して推論に任せる)
function calculateTotal2(items: number[]): number {
return items.reduce((sum, n) => sum + n, 0);
}
ポイントは次の通りです。
- 「戻り値を返すかどうか」を意識せずに
voidと書くと、コンパイルエラーで気づくことになります - 慣れないうちは 戻り値型の注釈を省略 して、TypeScriptの型推論に任せるのが安全です
- 関数の意図がはっきり「値を返さない」ときだけ、明示的に
voidと書きましょう
Promiseを待たずに呼んでしまう
非同期関数の戻り値型はPromise<void>になることが多いですが、awaitを忘れると処理順序が崩れます。
async function saveData(): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log("保存完了");
}
async function main() {
// NG: await なしで呼ぶと、保存完了を待たずに次の処理へ進む
saveData();
console.log("次の処理");
// 出力順: "次の処理" → "保存完了"
// OK: await で完了を待つ
await saveData();
console.log("次の処理");
// 出力順: "保存完了" → "次の処理"
}
ポイントは次の通りです。
- 戻り値型が
Promise<void>でも、 「値を返さない」だけで「処理が完了している」わけではありません - 完了を待ちたい場合は必ず
awaitを付けます awaitを忘れると、関数内で発生したエラー(Promiseのreject)が 未処理のまま になり、想定しない場所で例外が表面化することがあります
「値を返さない」をanyで書くのはNG
「voidが何か分からないからとりあえずanyで…」と書いてしまうのは、型安全性を捨ててしまうアンチパターンです。
// NG: 型安全性が損なわれる
function logMessage(message: string): any {
console.log(message);
}
const result = logMessage("hello");
// result の型は any なので、何を代入してもチェックされない
result.toUpperCase(); // 実行時エラーになる可能性があるが、型では検出できない
// OK: 値を返さないなら void
function logMessage2(message: string): void {
console.log(message);
}
const result2 = logMessage2("hello");
// result2 の型は void なので、誤った使い方を型で防げる
// result2.toUpperCase(); // 型エラーで気づける
ポイントは次の通りです。
anyは すべての型チェックを無効化する型 で、使うほど型安全性が下がります- 「値を返さない」という意図ならvoidを使うことで、誤った使い方を型レベルで防げます
よくある質問
TypeScriptのvoid型は何のために使うのですか?
void型は 「値を返さない関数の戻り値」を型として明示するため に使います。console.log系の関数のように「処理だけして何も返さない」関数の戻り値に付けることで、設計意図を型で表現でき、誤って値をreturnしてしまうミスをコンパイル時に防げます。
voidとundefinedはどちらを使えばよいですか?
戻り値を返さない意図ならvoid、明示的にundefinedという値を返したいならundefinedを使います。実務では関数の戻り値型としては voidを使うのが一般的 です。一方、変数やプロパティの型として「undefinedになる可能性がある」を表したいときはundefinedやT | undefinedの形を使います。
void型の関数でreturnを書いてもよいですか?
return;(値なしのreturn)はOKで、早期returnの用途で使えます。値を伴うreturn(例:return 42;)は原則NGでコンパイルエラーになります。例外としてreturn undefined;は許容されますが、わざわざ書く必要はあまりなく、 return文を省略するかreturn;を使う のが一般的です。
コールバック関数の引数でvoidになっているのに、値を返す関数を渡せるのはなぜですか?
() => voidは 「呼び出し側が戻り値を使わない」と宣言する型 だからです。実際の関数が値を返していても呼び出し側で使われないので問題なし、というのがTypeScriptの設計思想です。Array.prototype.forEachに値を返す関数を渡せるのもこの仕様のおかげで、実務的にとても便利な挙動になっています。
void型とnever型の違いは何ですか?
voidは「関数は正常終了するが値を返さない」、neverは「関数がそもそも正常終了しない(例外送出・無限ループ)」を表します。 戻ってくるならvoid、戻ってこないならnever と覚えてください。
Promise<void>はどう使いますか?
非同期関数で値を返さない場合の戻り値型として使います。たとえばデータを保存するだけで結果を返さない非同期関数の戻り値はPromise<void>になります。呼び出すときは必ずawaitを付けて完了を待ちましょう。
void型を引数の型注釈に書くこともできますか?
技術的には可能ですが、 実用性はほぼありません 。引数で受け取る値がvoid型だと、その引数を実質的に何にも使えないため、関数の引数として意味のある場面はまずありません。voidは戻り値型として使うものと考えて問題ないです。
【付録】さらに学びを深めるためのリソース
さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。
おすすめの書籍
ゼロからわかる TypeScript入門
技術評論社から出版されている「ゼロからわかる TypeScript入門」は、プログラミング初心者や本職プログラマーではない方を主な対象にした入門書です。
変数・条件分岐・ループといった基本から、クラスやインターフェース、モジュールまで段階的に学べる構成になっています。最終章ではWeb APIとJSONを使った非同期Webアプリの作成も体験できるので、「実際に動くものを作る」ところまで到達できます。
プロを目指す人のためのTypeScript入門
技術評論社の「プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで」、通称 ブルーベリー本 です。
JavaScriptの仕様とTypeScript独自の機能を両方押さえつつ、リテラル型・ユニオン型・keyof型・ジェネリクスなど、高度な型表現まで踏み込んで解説しています。TypeScriptの型システムの表現力を本格的に学べる一冊です。
オンラインで参照できる公式ドキュメント
TypeScript公式ハンドブック
https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。
TypeScript Deep Dive
https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。
TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。
まとめ
TypeScriptのvoid型は、関数を書くたびに登場する基本的な型でありながら、undefinedやneverとの違いやコールバックでの特殊ルールなど、初学者がつまずきやすいポイントを多く含んでいます。本記事のポイントを振り返ります。
- void型は「値を返さない関数の戻り値」を表す特別な型 であり、関数の意図を型で明示するために使う
- voidは undefinedの上位型 で、戻り値型がvoidの関数でも実行時にはundefinedが返る(型と実行時のズレ)
- コールバック関数の引数で
() => voidになっていても値を返す関数を渡せる のは、呼び出し側が戻り値を使わない宣言だから - never型は「そもそも関数が戻ってこない」 ことを表し、voidとは意味も型階層上の位置も別物
- 「戻り値があるのにvoid」「
Promise<void>を待たない」「voidの代わりにany」といったアンチパターンは型安全性を損なうので避ける
void型を理解できると、関数の戻り値の設計が一段とクリアになります。引き続き、neverやundefinedといった「値を返さない系」の型もセットで押さえていくと、TypeScriptの型システム全体の理解が深まります。
※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。






