Typescriptの例外処理

開発

例外とは

exceptionのことです。
たまに起きる失敗のことを指します。対して、必ず起きる失敗のことはバグと呼びます。

例外処理

エラーが起きたときに、プログラムを終了させずに決められた次の処理を行うことを、例外処理と呼びます。
try-catch構文とか、例外ハンドラということもあります。

例外処理の目的

例外から回復したり、例外が起きたことを通知したり、例外がどの部分でどんな例外が起きたのかを調査するために処理を追加します。
例外処理がなければ、例外が起きた場合プログラムは強制終了することになってしまいます。

例外処理の実装方法

それでは、例外処理はどのように実装していけばよいのでしょうか。

  • nullを返す
  • Errorクラスをthrowする
  • Errorクラスをreturnする

上記のとおり、オライリーのTypescript本に基本的な例外処理の実装方法が紹介されていたので、一部紹介します。
※アフィリンクではありません。

nullを返す

  • 一番シンプルな実装
  • 例外が発生したらnullを返す
  • デメリットとしては、以下2つ
  • 例外の詳細な情報を受け取れない
  • 複数の操作を組み立てることが難しい
    • 操作のあとでnullをチェックする仕組みを入れる必要があるので、複数処理を記述する際に冗長になりやすい

throwを使って、Errorクラスを投げる

  • 大体の言語にはthrow 句が用意されている
  • 投げるとか言ったりする
  • throwErrorクラス以外も投げることができる、普通の文字列とか
  • けど大体はErrorクラスもしくはそのサブクラスを投げる
  • throw に到達すると何が起こるか?
  • try-catch構文のtryの中で使われた場合は、処理を終了して即座にcatchへ飛ぶ
  • catchがない場合は、プログラムを強制終了させる
  • 例外に関するメタデータを取得できるようになる

コード例

// 引数の型が文字列型であれば`Error`クラスをthrowする、それ以外の型であれば引数をそのまま返す。
function getArgWithoutString(a: any) {
    if (typeof a === 'string') {
        throw new Error("arg is string");
    } else {
        return a;
    }
}

// getArgWithoutString関数に引数を渡す。引数が文字列であればErrorになる。
function Hoo (x: any) {
    try {
        let y = getArgWithoutString(x);
        console.log('Succeeded without error. Input was', y);
    }
    catch (e) {
        console.log('Error', e);
    }
}

// ここの引数を文字列かそれ以外にすることで処理の違いを確認できる
Hoo("test");
// 引数を文字列にした場合の出力結果
Error Error: arg is string
    at getArgWithoutString (/Users/moriha/Documents/iam-roler/src/tstt.ts:3:9)
    at Hoo (/Users/moriha/Documents/iam-roler/src/tstt.ts:11:11)
    at Object.<anonymous> (/Users/moriha/Documents/iam-roler/src/tstt.ts:19:1)
    at Module._compile (internal/modules/cjs/loader.js:1121:30)
    at Module.m._compile (/Users/moriha/Documents/iam-roler/node_modules/ts-node/src/index.ts:814:23)
    at Module._extensions..js (internal/modules/cjs/loader.js:1160:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/moriha/Documents/iam-roler/node_modules/ts-node/src/index.ts:817:12)
    at Module.load (internal/modules/cjs/loader.js:976:32)
    at Function.Module._load (internal/modules/cjs/loader.js:884:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:67:12)


Hoo(1);
//数字にした場合の出力結果
Succeeded without error. Input was 1

余談:Errorとそのサブクラスについて

  • Errorという標準クラスがある
  • これを継承したいくつかのサブクラスがある
  • RangeError
  • ReferenceError
  • SyntaxError
  • TypeError
  • URIError

例外を返す

  • 合併型を使うことで、関数の利用者に例外結果と成功結果をすべて処理させるように強制させることができる
  • 例外に関する十分な情報を提供できる

コード

  • 起こりうる例外を、parseの返り値の中に含める
  • スローされうる例外を利用者に事前に伝えることができる
  • それぞれの例外を処理することを利用者に強制させる
function parse( birthday: string ): Date | InvalidDateFormatError {
  let date = new Date(birthday)
  if(!isValid(date)) {
    return new InvalidDateFormatError('Error invalid date')
  }
  return date
}

let result = parse(ask()) //dateオブジェクトもしくはInvalidDateFormatError型になる
if(result instanceof InvalidDateFormatError){ //InvalidDateFormatError型だった場合
  console.error(resule.message)
} else {
  conosle.info('Sucsess')
}

おわりに

Typescriptの例外処理について紹介してみました。
これら以外にもプラクティスはありますが、どのプラクティスも使用箇所を考慮する必要があります。
自分もまだ勉強中なので、他の処理方法についても教えていただけると嬉しいです。

コメント

タイトルとURLをコピーしました