Typescript: Why is PromiseLike a possible type of this in an object in an async function?

Viewed 918

I wrote this function but it throws an error and I don't understand why...

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

Why does the function call in line 5 of this.bar() show an error? It tells me that this is either of type MyType or a PromiseLike<MyType>. The second possible type is the problem but I don't see in which case this might be a PromiseLike? Can't it assume that if that function is called, this is always an "loaded" object?

What is the most elegant way around this? Currently I am doing this (which is absolutely ugly and not understandable as the reader of the code):

await (await this).bar();

I also found out that setting the return type of the function createObject from Promise<MyType> to Promise<any> solves the problem but that isn't very accurate.

1 Answers

Since createObject is an async function returning Promise<MyType>, a return statement inside it can either return Promise<MyType> or MyType. Either is valid. So when you directly return an object literal, typescript somewhat counterintuitively will infer that the type of the object is Promise<MyType> | MyType, and will type this accordingly.

There is no way to change this if the object literal is directly in the return. What you can do is use an intermediary variable. This variable will be typed independent and then it will be checked against the return type:



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

Playground Link

Related