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 型 */ });