Retourner une promesse dans une fonction async en TypeScript


Il est de ma compréhension que ces deux fonctions auront le même comportement en JavaScript :

const whatever1 = (): Promise<number> => {
    return new Promise((resolve) => {
        resolve(4);
    });
};

const whatever2 = async (): Promise<number> => {
    return new Promise((resolve) => {
        resolve(4);
    });
};

Mais TypeScript semble ne pas aimer le deuxième, il dit :

Type '{}' is not assignable to type 'number'.

Est-ce un bug dans TypeScript, ou est-ce que je comprends mal quelque chose à propos des fonctions asynchrones ?

Réponse #1

C'est compliqué.

Tout d'abord, dans ce code

const p = new Promise((resolve) => {
    resolve(4);
});

le type de p est inféré comme Promise<{}>. Il y a un problème ouvert à ce sujet sur GitHub de TypeScript, donc on peut dire que c'est un bogue, car évidemment (pour un humain), p devrait être Promise<number>.

Alors, Promise<{}> est compatible avec Promise<number>, parce qu'en gros, la seule propriété qu'une promesse a est la méthode then, et then est compatible dans ces deux types de promesse selon les règles de TypeScript pour la compatibilité des types de fonctions. C'est pourquoi il n'y a pas d'erreur dans whatever1.

Mais le but de async est de faire semblant que vous traitez avec des valeurs réelles, pas des promesses, et ensuite vous obtenez l'erreur dans whatever2 parce que {} n'est évidemment pas compatible avec number.

Ainsi, le comportement async est le même, mais actuellement, une certaine solution de contournement est nécessaire pour que TypeScript le compile. Vous pourriez simplement fournir un argument générique explicite lors de la création d'une promesse comme ceci :

const whatever2 = async (): Promise<number> => {
    return new Promise<number>((resolve) => {
        resolve(4);
    });
};

Réponse #2

Lorsque vous faites new Promise((resolve)..., le type inféré était Promise<{}> parce que vous auriez dû utiliser new Promise<number>((resolve).

Il est intéressant de noter que ce problème n'a été mis en évidence que lorsque le mot clé async a été ajouté. Je recommanderais de signaler ce problème à l'équipe TS sur GitHub.

Il existe plusieurs façons de contourner ce problème. Toutes les fonctions suivantes ont le même comportement :

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);

Dans votre IDE, vous pourrez voir que le type inféré pour toutes ces fonctions est () => Promise<number>.