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.
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.