例外処理 (exception)
JavaScriptにはJavaに似た例外処理の構文があります。例外にはError
オブジェクトを使い、throw構文で例外を投げます。try-catch構文で例外を捕捉できます。
js
try {throw newError ("something wrong");} catch (e ) {// something wrongconsole .log (e .message );}
js
try {throw newError ("something wrong");} catch (e ) {// something wrongconsole .log (e .message );}
throw構文
JavaScriptのthrowは例外を投げる構文です。例外として投げるオブジェクトはErrorオブジェクトを使うのが一般的です。
js
throw newError ("network error!");
js
throw newError ("network error!");
JavaScriptのthrowはJavaなどと異なり、何でも投げることができます。プリミティブ型でさえ投げれます。
js
throw "just a string";
js
throw "just a string";
これはアンチパターンです。throwが何でも投げられるとしても、Errorオブジェクトを用いるべきです。Errorオブジェクトを使ったほうがコードの読み手に意外性を与えないからです。加えて、スタックトレースが追えるのはErrorオブジェクトだけだからです。
try-catch構文
JavaScriptで例外を捉えるにはtry-catch構文を使います。例外が投げられる可能性がある部分をtryブロックで囲み、catchブロックで捉えた例外に対する処理を行います。
js
try {throw newError ("something wrong");} catch (e ) {console .error (e );}
js
try {throw newError ("something wrong");} catch (e ) {console .error (e );}
catchの型
TypeScriptではcatchの変数の型はデフォルトでany
型になります。
ts
try {// ...} catch (e ) {}
ts
try {// ...} catch (e ) {}
型がError
オブジェクトの型ではなくany
型になるのは、JavaScriptの仕様上どんな値がthrowされるか分からないためです。
TypeScriptのコンパイラーオプションのuseUnknownInCatchVariables
を有効にすると、catchの変数の型がunknown
型になります。「どんな値がthrowされるか分からない」ことを型として正確に表現できるため、より型安全にしたい場合は、このオプションを有効化するとよいでしょう。
📄️ useUnknownInCatchVariables
📄️ undefined型
catchの分岐
JavaやPHPでは捉えるエラーの型に対応するcatchを複数書けますが、JavaScriptとTypeScriptではcatchは1つしか書けません。JavaScriptでエラーの型によってエラーハンドリングを分岐したい場合は、catchブロックの中で分岐を書く方法で対応します。
ts
try {// ...} catch (e ) {if (e instanceofTypeError ) {// TypeErrorに対する処理} else if (e instanceofRangeError ) {// RangeErrorに対する処理} else if (e instanceofEvalError ) {// EvalErrorに対する処理} else {// その他のエラー}}
ts
try {// ...} catch (e ) {if (e instanceofTypeError ) {// TypeErrorに対する処理} else if (e instanceofRangeError ) {// RangeErrorに対する処理} else if (e instanceofEvalError ) {// EvalErrorに対する処理} else {// その他のエラー}}
try-catchはブロックスコープ
JavaScriptのtry-catch文内の変数はブロックスコープになります。そのため、try-catch内で宣言された変数は、try-catchの外では参照できません。
ts
async functionfetchData () {try {constres = awaitfetch ("https://jsonplaceholder.typicode.com/todos/1");constdata = awaitres .json ();console .log (data ); // dataが参照できる} catch (e : unknown) {return;}Cannot find name 'data'.2304Cannot find name 'data'.console .log (); // dataが参照できない data }fetchData ();
ts
async functionfetchData () {try {constres = awaitfetch ("https://jsonplaceholder.typicode.com/todos/1");constdata = awaitres .json ();console .log (data ); // dataが参照できる} catch (e : unknown) {return;}Cannot find name 'data'.2304Cannot find name 'data'.console .log (); // dataが参照できない data }fetchData ();
📄️ 変数のスコープ
try-catch文の外でも変数を参照したい場合は、tryの前に代入用の変数をlet宣言しておく必要があります。
ts
async functionfetchData () {letdata : any;try {constres = awaitfetch ("https://jsonplaceholder.typicode.com/todos/1");data = awaitres .json ();} catch (e : unknown) {return;}console .log (data ); // dataが参照できる}fetchData ();
ts
async functionfetchData () {letdata : any;try {constres = awaitfetch ("https://jsonplaceholder.typicode.com/todos/1");data = awaitres .json ();} catch (e : unknown) {return;}console .log (data ); // dataが参照できる}fetchData ();
finallyブロック
JavaScriptにもJavaやPHPと同じようにfinallyが書けます。finallyは例外が発生しようがしまいが必ず実行される処理です。finallyはtry-catchの後に書きます。finally内の処理はtryとcatchの処理が実行された後に実行されます。
js
try {// ...} catch (e ) {// ...} finally {// ...}
js
try {// ...} catch (e ) {// ...} finally {// ...}