Niyas
Niyas

Reputation: 767

Firebase cloud function typescript error "Not all code paths return a value"

I am using firebase cloud function and firestore transaction to decrease the available quantity of product based on purchases. At the time of deploy it returns the error "error TS7030: Not all code paths return a value"

Here is the code

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


admin.initializeApp();
const db = admin.firestore()

exports.newOrder = functions.firestore
.document('orders/{orderId}')
.onCreate(async (snap, context) => {

    try {
        const data = snap.data();
        if (data === undefined) return null

        const itemList = data.list as Array<any>
        const productId: Set<string> = new Set<string>();

        itemList.forEach((item) => {
            productId.add(item.id)
        })
        return db.runTransaction(async t => {

            const promises: Promise<admin.firestore.DocumentSnapshot>[] = []
            productId.forEach(i => {
                const p = admin.firestore().doc('Products/' + i).get()
                promises.push(p)
            })
            const docs=await Promise.all(promises)
            docs.forEach(doc => {
                if (!doc.exists) {
                    return Promise.reject("Product deleted")
                }
            })
            itemList.forEach(j => {
                if (j.variation === '-') {
                    const available = docs[j.id].get('available')
                    const needed = j.quantity
                    if (available < needed) {
                        return Promise.reject("Product out of stock")
                    }
                }
                else {
                    const variations = docs[j.id].get('variation') as Map<string, any>
                    for (const i in variations.keys) {
                        if (i === j.variation) {
                            const needed = j.quantity
                            const available = docs[j.id].get('variation').get(i).get('quantity')
                            if (available < needed) {
                                return Promise.reject("Product out of stock")
                            }
                        }
                    }
                }
            })
            itemList.forEach(j => {
                if (j.variation === '-') {
                    const available = docs[j.id].get('available')
                    const needed = j.quantity
                    t.update(db.doc('Products/' + j.id), { 'available': available - needed })
                }
                else {
                    const variations = docs[j.id].get('variation') as Map<string, any>
                    for (const i in variations.keys) {
                        if (i === j.variation) {
                            const needed = j.quantity
                            const available = docs[j.id].get('variation').get(i).get('quantity')
                            t.update(db.doc('Products/' + j.id), { [`variation.${i}.quantity`]: available - needed })
                        }
                    }
                }
            })
            return Promise.resolve("Product quantity updated")
        })
    }
    catch (error) {
        console.log(`error ${error}`)
        return null
    }
});

Here is the error shown in deploy

 src/index.ts:30:30 - error TS7030: Not all code paths return a value.

 30                 docs.forEach(doc => {
                            ~~~~~~~~

 src/index.ts:35:34 - error TS7030: Not all code paths return a value.

 35                 itemList.forEach(j => {
                                ~~~~~~


 Found 2 error 

How to solve the error.

2 loops mentioned in the error checks whether the products is deleted or not and product is out of stock or not. If it satisfies the condition i want to exit from the function. Please help me.

Upvotes: 3

Views: 5576

Answers (3)

DRD
DRD

Reputation: 5813

This is a great example of Typescript imposing stupid conventions. JavaScript function outputs undefined by default when there is no explicit return statement. I recommend disabling return checking in tsconfig.json:

{
  "compilerOptions": {
    "noImplicitReturns": false,
  }
}

Upvotes: 3

Cerberus
Cerberus

Reputation: 10218

The probles is deeper than it may seem to be. You're possibly misunderstanding what does return statement do inside the forEach. The code is structured as if it was assumed to check if any of the doc.exists is false and return early if so, but written as this, it will return from the iteration callback.. and since forEach doesn't use the callback return value, the promise rejection remains unhandled.

The proper ways of achieving this result would be the following:

1) Just check directly what you need to check:

if (docs.findIndex(doc => !doc.exists) !== -1) {
    return Promise.reject("Product deleted");
}

2) Use the for..in or for..of loop instead of forEach:

for (doc of docs) {
    if (!doc.exists) {
        return Promise.reject("Product deleted")
    }
}

3) Use the map and await the result (not recommended, since you don't really need mapping):

await Promise.all(docs.map(doc => {
    if (!doc.exists) {
        return Promise.reject("Product deleted")
    }
    return null
})

Note that in this case any rejected promise inside the resulting array should trigger the rejection of the outer promise.


Side note: you don't ever need the explicit Promise.reject() call. As your function is async, you can simply throw something you'll use as an error - this will be transformed into promise rejection anyway.

Upvotes: 4

Doug Stevenson
Doug Stevenson

Reputation: 317372

The error message is telling you that there are functions that don't return a value in all situations. It's even telling which functions are violating this requirement. Here's the first error:

 src/index.ts:30:30 - error TS7030: Not all code paths return a value.

 30                 docs.forEach(doc => {
                            ~~~~~~~~

It's telling you that the function you're passing to forEach is having a problem.

Here's the function:

        docs.forEach(doc => {
            if (!doc.exists) {
                return Promise.reject("Product deleted")
            }
        })

Note that the function isn't returning a value when doc.exists is true. If you don't care about this situation, just return null:

        docs.forEach(doc => {
            if (!doc.exists) {
                return Promise.reject("Product deleted")
            }
            else {
                return null
            }
        })

Now the error goes away, because all code paths return a value. You can apply the same logic to the other error.

Upvotes: 5

Related Questions