自分でエラーを投げる
このレッスンで分かること
throwは無視できないので、続けてはいけない失敗を確実に止められますErrorを継承したクラスを作ると、失敗の種類ごとに情報を持たせられます
false を返しても、呼ぶ側は先へ進める
入力の検証を書くとき、最初に思いつくのは false を返す形です。
JavaScript
function checkTitle(title) {
return title !== "";
}
checkTitle(work.title);
saveWork(work);このコードは動きます。ただし戻り値を見ずに次の行へ進めてしまいます。呼ぶ側が if を 1 行書き忘れるだけで、検証は素通りです。しかも false からは、何がだめだったのかが分かりません。見る項目が増えると、どこでつまずいたのかを別の戻り値で運ぶことになり、戻り値の形がふくらんでいきます。
throw は逆で、無視できません。投げた時点でその先の行は実行されないので、呼ぶ側が受け止めなければそこで止まります。
戻り値で返すか投げるかは、呼ぶ側がそのまま続けてよいかで決めます。作品が 0 件なのは失敗ではないので戻り値です。タイトルが空のまま保存しようとしたのは続けてはいけないので
throwです。
投げてよいのは Error とその仲間だけ
throw はどんな値でも投げられますが、文字列を投げてはいけません。
JavaScript
throw "タイトルが空です";こう書くと、受け取った側の error.name も error.message も undefined になります。stack も無いので、どこで起きたかも分かりません。捕まえる側が毎回「文字列かもしれない」と身構えることになります。最初から用意されている種類は次のとおりです。
| クラス | 使いどころ |
|---|---|
Error | どれにも当てはまらないとき |
TypeError | 渡された値の型や形が違うとき |
RangeError | 値が許される範囲の外にあるとき |
Error を継承して、失敗に情報を持たせる
用意されている種類では足りないときは、自分で作ります。書き方はふつうのクラスと同じです。
JavaScript
class UploadError extends Error {
constructor(fileName, message) {
super(message);
this.name = "UploadError";
this.fileName = fileName;
}
}
const failure = new UploadError("photo.png", "画像が大きすぎます");
console.log(failure.name, failure.fileName); // UploadError photo.png見どころは 3 つあります。super(message) を先頭で呼ぶこと。呼ばないと error.message が空になります。this.name を自分で書き換えること。書かないと継承元のまま "Error" なので、名前で見分けられません。そして必要な情報を好きなプロパティとして足せること。上の fileName は、画面のどこを赤くすればよいかを受け取る側へ伝えるために使います。
捕まえた側は instanceof で見分けます。
JavaScript
try {
assertFileSize(file);
} catch (error) {
if (error instanceof UploadError) {
highlightField(error.fileName, error.message);
} else {
showNotice("想定していないエラーです");
}
}順番に注意します。UploadError は Error でもあるので、error instanceof Error を先に書くと自前のクラスがそこで吸われてしまいます。細かいほうから先に並べます。
else のほうも大事です。単純な書き間違いで TypeError が飛んだときに「入力が正しくありません」と表示してしまうと、利用者は何度直しても直らない画面と向き合うことになります。想定した失敗と想定外の失敗は、必ず別の文言にします。
種類を増やしすぎないでください。画面の出し分けが変わらないなら分ける必要はありません。捕まえた側で分岐を書きたくなったときに、初めて 1 つ増やします。
よくある間違い
this.nameを書き忘れる —nameは継承元の値がそのまま残るので"Error"と出ます。名前で見分けたいなら自分で入れますsuperより先にプロパティを触る —this.fileName = fileName;をsuper(message)より上に書くとReferenceErrorです。superは必ず先頭に置きます- 止めるつもりで
console.logを書く —console.logは処理を止めません。その下の行がそのまま動くので、不備のある値を抱えたまま先へ進みます。止めたいならthrowです
要件
- ValidationError というクラスを Error から継承して作り、field と message を持たせる。name は "ValidationError" にする
- reviewWork(work) を作り、戻り値は { ok, field, message } の 3 つのキーを持つオブジェクトにする
- work がオブジェクトでない場合は ok を false、field を null、message を "想定していないエラーです" にする
- title が空文字列なら field は "title"、message は "タイトルを入力してください" にする
- year が数値でなければ "公開年は数値で入力してください"、2000 未満なら "公開年が古すぎます" にする。どちらも field は "year"
- 不備が無ければ ok を true、field を null、message を空文字列にする