4

I've created the following function to delete user and it's dedicated collection which works fine.

import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';

admin.initializeApp();

const Auth = admin.auth();
const UsersCollection = admin.firestore().collection(`users`);

exports.deleteUser = functions.https.onCall((data, context) => {
  if (context.auth) {
    const userID = context.auth.uid;

    Auth.deleteUser(userID)
      .then(() => {
        UsersCollection.doc(userID).delete()
          .catch(error => {
            console.log(error)
          })
      })
      .catch(error => {
        console.log(error)
      })
  }
});

Then I've tried to overwrite in into promise.all so the user's collection deletion doesn't has to wait until the user is actually delete. So I've tried to do the following:

exports.deleteUser = functions.https.onCall((data, context) => {
  if (context.auth) {
    const userID = context.auth.uid;
    const promises = [];
    const deleteCollection = UsersCollection.doc(userID).delete();

    promises.push(Auth.deleteUser(userID));
    promises.push(deleteCollection);

    return Promise.all(promises)
  }
});

During the build of this magic the console output the following error from TSLint:

src/index.ts:35:24 - error TS2769: No overload matches this call.
  The last overload gave the following error.
    Argument of type '(Promise<void> | Promise<WriteResult>)[]' is not assignable to parameter of type 'Iterable<void | PromiseLike<void>>'.
      Types of property '[Symbol.iterator]' are incompatible.
        Type '() => IterableIterator<Promise<void> | Promise<WriteResult>>' is not assignable to type '() => Iterator<void | PromiseLike<void>, any, undefined>'.
          Type 'IterableIterator<Promise<void> | Promise<WriteResult>>' is not assignable to type 'Iterator<void | PromiseLike<void>, any, undefined>'.
            Types of property 'next' are incompatible.
              Type '(...args: [] | [undefined]) => IteratorResult<Promise<void> | Promise<WriteResult>, any>' is not assignable to type '(...args: [] | [undefined]) => IteratorResult<void | PromiseLike<void>, any>'.
                Type 'IteratorResult<Promise<void> | Promise<WriteResult>, any>' is not assignable to type 'IteratorResult<void | PromiseLike<void>, any>'.
                  Type 'IteratorYieldResult<Promise<void> | Promise<WriteResult>>' is not assignable to type 'IteratorResult<void | PromiseLike<void>, any>'.
                    Type 'IteratorYieldResult<Promise<void> | Promise<WriteResult>>' is not assignable to type 'IteratorYieldResult<void | PromiseLike<void>>'.
                      Type 'Promise<void> | Promise<WriteResult>' is not assignable to type 'void | PromiseLike<void>'.
                        Type 'Promise<WriteResult>' is not assignable to type 'void | PromiseLike<void>'.
                          Type 'Promise<WriteResult>' is not assignable to type 'PromiseLike<void>'.
                            Types of property 'then' are incompatible.
                              Type '<TResult1 = WriteResult, TResult2 = never>(onfulfilled?: ((value: WriteResult) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<...>' is not assignable to type '<TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => PromiseLike<...>'.
                                Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
                                  Types of parameters 'value' and 'value' are incompatible.
                                    Type 'WriteResult' is not assignable to type 'void'.

35     return Promise.all(promises)
                          ~~~~~~~~

  node_modules/typescript/lib/lib.es2015.iterable.d.ts:226:5
    226     all<TAll>(values: Iterable<TAll | PromiseLike<TAll>>): Promise<TAll[]>;
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    The last overload is declared here.


Found 1 error.

As far as I understood it doesn't like promises.push(deleteCollection); since removing removes the TSLint issue. I would appreciate a lot if somebody could clarify how to deal with issues like this?

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
volna
  • 2,452
  • 4
  • 25
  • 61
  • 1
    Try to change `promises.push(deleteCollection);` to `promises.push(UsersCollection.doc(userID).delete())` – Emeric Sep 09 '19 at 11:49
  • Thank you for the suggestion @Curse I've tried it before and it still outputted the error :) – volna Sep 09 '19 at 12:08
  • 1
    What happend if you just execute `UsersCollection.doc(userID).delete();` without `Auth.deleteUser(userID)` ? – Emeric Sep 09 '19 at 12:17
  • The issue is actually, gone. Probably it complains of having more than 1 promise in the array, I've added `promises.push(console.log(1))` and the error came back. – volna Sep 09 '19 at 13:39
  • 1
    Maybe https://stackoverflow.com/questions/33684634/how-to-use-promise-all-with-typescript is relevant – apokryfos Sep 09 '19 at 14:30

2 Answers2

6

Tell TypeScript that the array of promises can contain promises that return any type of value:

const promises: Promise<any>[] = [];

Also you might have to tell TypeScript that you wish to return a value to the client in all code paths:

if (context.auth) {
    // ...
    return Promise.all(promises)
}
else {
    return { error: "No authenticated user" }
}

Bear in mind that that for callable type functions, the value of the resolved promise is going to get serialized and sent to the client, so you should think carefully if that's what you really want to happen.

Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441
0

It's easier and more precise types should get inferred if you just create the promises with elements you want:

const userID = context.auth.uid;
const deleteCollection = UsersCollection.doc(userID).delete();

const promises = [Auth.deleteUser(userID)), deleteCollection];

return Promise.all(promises);

Or you can even remove the promises variable:

return Promise.all([Auth.deleteUser(userID)), deleteCollection]);

The type will be even more precise with a const assertion,

const promises = [Auth.deleteUser(userID)), deleteCollection] as const;
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487