Reputation: 767
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
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
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
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