TypeScript の Promise の型 だと、型パラメータが Promise#then
側しかなくて、 Promise#catch
側の型が any
になってしまって不便だ。
もし、catch
側の型についても型引数で指定できたなら、より安全なプログラミングができる。
そこで、catch
側の型を指定していない理由について考察してみた。
理想
const promise: IdealPromise<T, E> = getPromise(); promise .then((x) => { /* x は T 型 */ }); promise .catch((e) => { /* e は E 型 */ });
実際
const promise: Promise<T> = getPromise(); promise .then((x) => { /* x は T 型 */ }); promise .catch((e) => { /* e は any 型 */ });
考察
前提条件
TypeScript/JavaScript には以下の制約がある:
- 制約1: TypeScript は関数の評価時に発生しうる例外の型を検査しない
- 制約2: JavaScript はなんでも
throw
できる(例:throw null
) - 制約3:
new Promise(fn)
に与えられた関数fn
が例外e
を発生させた時、このPromise
インスタンスは理由e
で棄却状態になる
帰結
これらの制約の上でうまく型付けしようとすると、catch
側は any
にならざるをえない。
TypeScript の型検査器は、new Promise(fn)
の fn
が発生しうる例外の型を推定する材料を持っていない。
JavaScript はどんな値・オブジェクトも例外として発生させられるので、fn
から発生しうる例外の型は any
であると推論するほかない。
そして、この fn
が例外 e
を発生させたとき、この Promise は e
で棄却状態になる。
このとき、e
の型は、前述の通り any
としか推論できない。したがって、catch の引数の型は常に any である。
であれば、catch 側の型引数は必要ない。
理想に近づくためには
TypeScript に例外の型宣言ができるようになれば、できそうな気がする?
例えば:
function resolver<T, E>(resolve: (x: T) => void, reject: (e: E) => void): a throws E { // ... } const promise: Promise<T, E> = new Promise(resolver); promise .then((x) => { /* x は T 型 */ }); promise .catch((e) => { /* e は E 型 */ });