noImplicitReturns
noImplicitReturnsは、直訳すると「暗黙的なreturnの禁止」という意味で、関数のすべての分岐できちんと値を返しているかを検査するオプションです。
- デフォルト:
false - 追加されたバージョン: 1.8
解説
JavaScriptでは、関数が明示的にreturnをしなくても、暗黙的にundefinedを返す仕様があります。
jsfunctiondoSomething () {// return文がない}constvalue =doSomething ();console .log (value );
jsfunctiondoSomething () {// return文がない}constvalue =doSomething ();console .log (value );
つまり、上の関数は次のようなreturn undefinedを書いた関数と同じ意味になります。
jsfunctiondoSomething () {returnundefined ;}
jsfunctiondoSomething () {returnundefined ;}
この仕様にはひとつ問題があります。「本当にundefinedを返したくてreturn文を省略したのか」「return文を書き忘れて、意図せずundefinedを返しているのか」がはっきりしないという問題です。もし単なる書き忘れだった場合は、バグにつながります。
jsfunctiongetValue (map ,key ) {if (key inmap ) {returnmap [key ];}// この経路では、undefinedを返すことを意図している?// それとも本当はnullを返したかった?// もしくは例外を投げるべきだった?}
jsfunctiongetValue (map ,key ) {if (key inmap ) {returnmap [key ];}// この経路では、undefinedを返すことを意図している?// それとも本当はnullを返したかった?// もしくは例外を投げるべきだった?}
そのため、JavaScriptのベストプラクティスとしては、本当にundefinedを返すつもりなら、明示的にreturn文を書くことが推奨されています。
jsfunction getValue(map, key) {if (key in map) {return map[key];}return undefined;}
jsfunction getValue(map, key) {if (key in map) {return map[key];}return undefined;}
関数が複雑になるほど、どこかの分岐でreturnの書き忘れ事故が発生しやすくなります。次の例はさほど複雑でないものの、「境界値の処理を忘れる」という典型的なミスを含んだ例です。
tsfunctionnegaposi (num : number) {if (num > 0) {return "positive";}if (num < 0) {return "negative";}// num === 0 の経路で return を書き忘れている// → この関数は暗黙的に undefined を返す}console .log (negaposi (0));
tsfunctionnegaposi (num : number) {if (num > 0) {return "positive";}if (num < 0) {return "negative";}// num === 0 の経路で return を書き忘れている// → この関数は暗黙的に undefined を返す}console .log (negaposi (0));
noImplicitReturnsを有効にすると、returnを書き忘れた関数を検出できるようになります。たとえば、次のようなコードはエラーになります。
tsfunctionNot all code paths return a value.7030Not all code paths return a value.( negaposi num : number) {if (num > 0) {return "positive";}if (num < 0) {return "negative";}// return忘れ}
tsfunctionNot all code paths return a value.7030Not all code paths return a value.: number) { negaposi (num if (num > 0) {return "positive";}if (num < 0) {return "negative";}// return忘れ}
すべての経路で値を返すように修正すると、コンパイルが通ります。
tsfunctionnegaposi (num : number) {if (num > 0) {return "positive";}if (num < 0) {return "negative";}return "zero"; // return漏れを修正}
tsfunctionnegaposi (num : number) {if (num > 0) {return "positive";}if (num < 0) {return "negative";}return "zero"; // return漏れを修正}
returnなしが許容されるケース
noImplicitReturnsを有効にした場合でも、利便性のためにreturnなしが許容されるケースがあります。
まず、throwで終わる分岐は許容されます。
tsfunctionnegaposi (num : number) {if (num > 0) {return "positive";}if (num < 0) {return "negative";}throw newError ("this is 0"); // returnなしでもエラーにならない}
tsfunctionnegaposi (num : number) {if (num > 0) {return "positive";}if (num < 0) {return "negative";}throw newError ("this is 0"); // returnなしでもエラーにならない}
次に、戻り値の型注釈がvoidの場合もreturnなしが許容されます。
tsfunctionlog (message ?: string): void {// ^^^^型注釈if (!message ) {return;}console .log (message );// returnなしでもエラーにならない}
tsfunctionlog (message ?: string): void {// ^^^^型注釈if (!message ) {return;}console .log (message );// returnなしでもエラーにならない}
戻り値の型注釈がstring | voidのようなユニオン型の場合もreturnなしの経路が許容されます。
tsfunctionnegaposi (num : number): string | void {// ^^^^^^^^^^^^^型注釈if (num > 0) {return "positive";}if (num < 0) {return "negative";}// returnなしでもエラーにならない}
tsfunctionnegaposi (num : number): string | void {// ^^^^^^^^^^^^^型注釈if (num > 0) {return "positive";}if (num < 0) {return "negative";}// returnなしでもエラーにならない}
voidがユニオン型に含まれない場合、たとえundefinedが含まれていても、何も返さない経路があるとエラーになります。
tsfunctionNot all code paths return a value.7030Not all code paths return a value.negaposi (num : number): string | undefined {if (num > 0) {return "positive";}if (num < 0) {return "negative";}// ここにreturnが必要}
tsfunctionNot all code paths return a value.7030Not all code paths return a value.negaposi (num : number): string | undefined {if (num > 0) {return "positive";}if (num < 0) {return "negative";}// ここにreturnが必要}
noImplicitReturnsの警告を抑えるために、戻り値の型をvoidと別の型で構成するよりも、しっかりreturn undefinedを書き、戻り値の型注釈もstring | undefinedとするほうが、意外性の少ないコードになるので、特に事情がない限りそう書くようにしましょう。
tsfunctionnegaposi (num : number): string | undefined {if (num > 0) {return "positive";}if (num < 0) {return "negative";}returnundefined ;}
tsfunctionnegaposi (num : number): string | undefined {if (num > 0) {return "positive";}if (num < 0) {return "negative";}returnundefined ;}
最後に、戻り値の型注釈がanyの場合もreturnなしが許容されます。
tsfunctionlog (message ?: string): any {// ^^^型注釈if (!message ) {return;}console .log (message );// returnなしでもエラーにならない}
tsfunctionlog (message ?: string): any {// ^^^型注釈if (!message ) {return;}console .log (message );// returnなしでもエラーにならない}
注意点としてanyはnoImplicitReturnsの警告を抑えるだけでなく、他の型チェックも放棄するので、コードの安全性を損なう可能性があります。特段の理由がない限り、noImplicitReturnsについての警告を抑えるためだけにanyを使うのは避けましょう。