メインコンテンツまでスキップ

戻り値がない関数とvoid型 (void type)

TypeScriptで戻り値がない関数の戻り値を型注釈するにはvoid型を用います。void型は関数の戻り値を型注釈するためにある特別な型です。

ts
function print(message: string): void {
console.log(message);
}
ts
function print(message: string): void {
console.log(message);
}

JavaScriptでは、戻り値がない関数を呼び出したとき、その関数から返る値はundefinedです。

ts
function fn() {
// 戻り値のない関数
}
const result = fn();
console.log(result);
undefined
ts
function fn() {
// 戻り値のない関数
}
const result = fn();
console.log(result);
undefined

しかし、TypeScriptでは、このような戻り値がない関数の戻り値の型注釈にはvoidを用いるのが一般的です。

ts
function fn(): void {
// 戻り値のない関数
}
ts
function fn(): void {
// 戻り値のない関数
}

undefined型とvoid型の違い

void型の代わりに、undefined型を関数の戻り値の型注釈に用いる書き方もできます。ただし、これは一般的な書き方ではありません。

戻り値がundefinedのときはreturnが必須

戻り値型がundefined型の場合は、returnが無いとコンパイルエラーになります。この点はvoid型と異なる点です。

ts
function fn(): undefined {}
ts
function fn(): undefined {}

このコンパイルエラーは、returnを加えることで解消しますが、戻り値を返さないことを意図するのであれば、void型を使うほうがよいです。

ts
function fn(): undefined {
return;
}
ts
function fn(): undefined {
return;
}

戻り値がundefinedを含みうる関数の場合は、undefined型を含んだユニオン型を使うのが一般的です。

ts
function getIfExists(numbers: number[], search: number): number | undefined {
if (numbers.includes(search)) {
return search;
}
return undefined;
}
ts
function getIfExists(numbers: number[], search: number): number | undefined {
if (numbers.includes(search)) {
return search;
}
return undefined;
}

voidはundefinedの上位型

void型は関数戻り値の型注釈にだけ使うのが普通です。変数の型注釈に使うことはまずありません。しかし、もしも変数の型注釈にvoid型を使った場合、voidとundefinedは異なる型になります。undefined型はvoid型に代入できる一方、void型はundefined型に代入できません。これを一言でいうと、voidはundefinedの上位型(supertype)ということになります。

ts
const v: void = undefined; // undefined型はvoid型に代入できる
const u: undefined = v; // void型はundefined型に代入できない
Type 'void' is not assignable to type 'undefined'.2322Type 'void' is not assignable to type 'undefined'.
ts
const v: void = undefined; // undefined型はvoid型に代入できる
const u: undefined = v; // void型はundefined型に代入できない
Type 'void' is not assignable to type 'undefined'.2322Type 'void' is not assignable to type 'undefined'.

この特徴は、関数の誤用に気づくきっかけを与えてくれます。たとえば、次の2つの関数を考えてみましょう。どちらも戻り値なしを意図した関数です。処理内容も同じです。違いは、f1は型注釈がvoidですが、f2undefinedです。

ts
function f1(): void {}
function f2(): undefined {
return;
}
ts
function f1(): void {}
function f2(): undefined {
return;
}

これらの関数を呼び出すとき、戻り値を受け取るように書けるものの、これら関数の使い方としては正しくないでしょう。次のコードは、戻り値を変数に代入しようとしています。これは誤ったコードです。

ts
let mayBeNumber: number | undefined;
mayBeNumber = f1(); // 誤った関数の使い方
mayBeNumber = f2(); // 誤った関数の使い方
ts
let mayBeNumber: number | undefined;
mayBeNumber = f1(); // 誤った関数の使い方
mayBeNumber = f2(); // 誤った関数の使い方

このとき、型注釈がvoidf1の呼び出し部分はコンパイルエラーとなります。これにより、誤りに気づきやすくなります。

ts
let mayBeNumber: number | undefined;
mayBeNumber = f1(); // コンパイルで誤りに気づける
Type 'void' is not assignable to type 'number | undefined'.2322Type 'void' is not assignable to type 'number | undefined'.
mayBeNumber = f2(); // コンパイルでは誤りに気づけない
ts
let mayBeNumber: number | undefined;
mayBeNumber = f1(); // コンパイルで誤りに気づける
Type 'void' is not assignable to type 'number | undefined'.2322Type 'void' is not assignable to type 'number | undefined'.
mayBeNumber = f2(); // コンパイルでは誤りに気づけない

このような観点からも、戻り値がない関数を宣言するときはvoidを使ったほうがよいわけです。