若くない何かの悩み

何かをdisっているときは、たいていツンデレですので大目に見てやってください。

TypeScript の Promise<T> が Promise<T, E> ではない理由の心当たり

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

SEE ALSO

github.com github.com github.com