TypeScriptで非同期関数でPromiseを返すこと
私の理解では、これら二つの関数はJavaScriptで同じ動作をするはずです。
const whatever1 = (): Promise<number> => {
return new Promise((resolve) => {
resolve(4);
});
};
const whatever2 = async (): Promise<number> => {
return new Promise((resolve) => {
resolve(4);
});
};
しかし、TypeScript は2つ目の方を好まないようで、次のように言います:
Type '{}' is not assignable to type 'number'.
これはTypeScriptのバグですか、それとも私が非同期関数について何かを誤解しているのでしょうか?
回答 #1
複雑です。
まず第一に、このコードでは
const p = new Promise((resolve) => {
resolve(4);
});
p の型は Promise<{}> と推論されます。この件については TypeScript の GitHub にオープンな問題があります。したがって、人間から見れば明らかに p は Promise<number> であるべきなので、これはバグであると言えるでしょう。
それなら、Promise<{}> は Promise<number> と互換性があります。なぜなら、基本的にプロミスが持つ唯一のプロパティは then メソッドであり、TypeScript の関数型互換性ルールに従って、この then メソッドはこれら二つのプロミスタイプで互換性があるからです。だから whatever1 にはエラーがないのです。
しかし、async の目的は、実際の値を扱っているかのように振る舞うことであり、その後 whatever2 でエラーが発生するのは、{} が明らかに number と互換性がないからです。
つまり、async の動作は同じですが、現時点では TypeScript にコンパイルさせるためにいくつかの回避策が必要です。プロミスを作成するときに、単純に明示的なジェネリック引数を提供することができます。
const whatever2 = async (): Promise<number> => {
return new Promise<number>((resolve) => {
resolve(4);
});
};
回答 #2
new Promise((resolve)... を実行すると、型推論は Promise<{}> となりました。なぜなら、new Promise<number>((resolve) を使用するべきだったからです。
この問題が async キーワードが追加されたときにのみ指摘されたのは興味深いです。この問題を GitHub 上の TS チームに報告することをお勧めします。
この問題を回避する方法はいくつもあります。以下のすべての関数は同じ動作をします:
const whatever1 = () => {
return new Promise<number>((resolve) => {
resolve(4);
});
};
const whatever2 = async () => {
return new Promise<number>((resolve) => {
resolve(4);
});
};
const whatever3 = async () => {
return await new Promise<number>((resolve) => {
resolve(4);
});
};
const whatever4 = async () => {
return Promise.resolve(4);
};
const whatever5 = async () => {
return await Promise.resolve(4);
};
const whatever6 = async () => Promise.resolve(4);
const whatever7 = async () => await Promise.resolve(4);
あなたのIDEでは、これらすべての関数の推論された型が () => Promise<number> であることを確認できます。
