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