12

I expect type void[] to be compatible with type void. Specifically, when using Promise.all.

class Foo {
  delete(): Promise<void> {
    // do delete
    return Promise.resolve();
  }
  deleteMany(list: Foo[]): Promise<void> {
    return Promise.all(list.map((x) => x.delete()));
  }

typescript error:

'Type 'Promise<void[]>' is not assignable to type 'Promise<void>'. Type 'void[]' is not assignable to type 'void'.'

I can solve this two ways that I know of:

  1. Mark deleteMany as returning Promise<void[]>.
  2. Promise chain to Promise.all, to return a resolved promise. e.g.

    return Promise.all(list.map((x) => x.delete())).then(() => Promise.resolve());
    

The second one is worse, since that bit of code executes in JavaScript, but the first one is confusing to developers. Does Typescript have poor support for Promise.all or is it omitted from their documentation? Anyone find a better solution?

6
  • 1
    Why not just mark deleteMany as returning Promise<void[]>? That's not poor support, that's what a Promise.all call returns... Commented Apr 24, 2017 at 20:56
  • As @MikeMcCaughan suggests.. I agree.. Promise.all returns a Promise of array of the type, which in this case it is void Commented Apr 24, 2017 at 20:57
  • 3rd option: Just leave the return type off entirely. The compiler will infer it and raise an error if it is used incorrectly. Go with 1 or 3, 2 does not make sense. Commented Apr 24, 2017 at 21:11
  • From the MDN docs: "If all of the passed-in promises fulfill, Promise.all is fulfilled with an array of the values from the passed-in promises, in the same order as defined in the iterable." This is the expected result of Promise.all -- a promise that resolves to an array containing the fulfilled values which in this case will all be void. It would be confusing to developers to type it with anything BUT Promise<void[]> Commented Apr 24, 2017 at 21:34
  • 2
    @MikeMcCaughan Because consumers shouldn't care about implementation detail. I like Aluan's suggestion the best. sbking You're absolutely right, however, I find arrays filled with void types terribly unhelpful. Commented Apr 27, 2017 at 21:04

3 Answers 3

8

The 1 (Mark deleteMany as returning Promise<void[]>) is absolutely ok at least for me.

However, you can use async/await if you really want to return Promise<void>:

class Foo {
  delete(): Promise<void> {
    // do delete
    return Promise.resolve();
  }
  async deleteMany(list: Foo[]): Promise<void> {
    await Promise.all(list.map((x) => x.delete()));
  }

The function will behave exactly the same way.

Sign up to request clarification or add additional context in comments.

Comments

4

void is incompatible with any other type except null and undefined by design, so it's incompatible with void[].

You could use Promise<{}> as result type of deleteMany, that is, promise resolving to empty object. It works, because empty object type has no properties (so it can not meaningfully be used in any way), and, because it has no properties (again), it could be assigned almost anything, including void[]:

class Foo {
    delete(): Promise<void> {
        // do delete
        return Promise.resolve();
    }
    deleteMany(list: Foo[]): Promise<{}> {
        return Promise.all(list.map((x) => x.delete()));
    }
}

Comments

0

Since the promise doesn't actually contains data, you can safely cast like this:

class Foo {
  delete(): Promise<void> {
    // do delete
    return Promise.resolve();
  }
  deleteMany(list: Foo[]): Promise<void> {
    return (Promise.all(list.map((x) => x.delete()))) as unknown as Promise<void>;
  }
}

The intermediate unknown can trick the TS compiler to allow casting

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.