Typescript : Pourquoi PromiseLike est-il un type possible de this dans un objet dans une fonction asynchrone ?


J'ai écrit cette fonction mais elle génère une erreur et je ne comprends pas pourquoi...

const createObject = async (): Promise<MyType> => {
  await Promise.resolve();
  return {
    async foo() {
      await this.bar();
      // do something else
    },
    async bar() {
      await Promise.resolve();
      // do something here
    }
  };
};

interface MyType {
  foo: () => Promise<void>;
  bar: () => Promise<void>;
}

Pourquoi l'appel de fonction à la ligne 5 de this.bar() affiche-t-il une erreur ? Il me dit que this est soit de type MyType, soit de type PromiseLike<MyType>. Le deuxième type possible est le problème, mais je ne vois pas dans quel cas ceci pourrait être un PromiseLike ? Ne peut-il pas supposer que si cette fonction est appelée, this est toujours un objet « chargé » ?

Quelle est la manière la plus élégante de contourner cela ? Actuellement, je fais ceci (ce qui est absolument moche et incompréhensible pour le lecteur du code) :

await (await this).bar();

J'ai aussi découvert que définir le type de retour de la fonction createObject de Promise<MyType> à Promise<any> résout le problème, mais ce n'est pas très précis.

Réponse #1

Puisque createObject est une fonction asynchrone retournant Promise<MyType>, une instruction return à l'intérieur peut soit retourner Promise<MyType> soit MyType. Les deux sont valides. Donc, lorsque vous retournez directement un littéral d'objet, TypeScript déduira de manière quelque peu contre-intuitive que le type de l'objet est Promise<MyType> | MyType, et typera this en conséquence.

Il n'y a aucun moyen de changer cela si le littéral d'objet est directement dans le retour. Ce que vous pouvez faire est d'utiliser une variable intermédiaire. Cette variable sera typée de manière indépendante puis elle sera vérifiée par rapport au type de retour :


const createObject = async (): Promise<MyType> => {
  await Promise.resolve();
  let result: MyType = {
    async foo() {
      await this.bar();
      // do something else
    },
    async bar() {
      await Promise.resolve();
      // do something here
    }
  };
  return result;
};

interface MyType {
  foo: () => Promise<void>;
  bar: () => Promise<void>;
}

Lien du terrain de jeu