خطای تایپاسکریپت تابع ابری Firebase «تمام مسیرهای کد مقداری برنمیگردانند»


من از فانکشن ابری Firebase و تراکنش Firestore برای کاهش مقدار موجودی محصول بر اساس خریدها استفاده میکنم. هنگام استقرار، خطای «error TS7030: Not all code paths return a value» را برمیگرداند.

در اینجا کد است

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
    }
});

اینجا خطایی که در هنگام استقرار نشان داده شده است

 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 

چگونه خطا را حل کنیم.

۲ حلقه ذکر شده در خطا بررسی میکنند که آیا محصول حذف شده است یا نه و آیا محصول موجود نیست یا نه. اگر شرایط را برآورده کند، میخواهم از تابع خارج شوم. لطفاً به من کمک کنید.

پاسخ شماره ۱

پیغام خطا به شما میگوید که توابعی وجود دارند که در همه شرایط مقداری باز نمیگردانند. حتی به شما میگوید که کدام توابع این الزام را نقض میکنند. اینجا اولین خطا آمده است:

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

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

این به شما میگوید که تابعی که به forEach میدهید مشکل دارد.

این هم تابع:

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

توجه داشته باشید که این تابع زمانی که doc.exists درست است هیچ مقداری برنمیگرداند. اگر برای شما این وضعیت مهم نیست، فقط مقدار null را برگردانید:

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

اکنون خطا از بین میرود، زیرا تمامی مسیرهای کد یک مقدار برمیگردانند. میتوانید همان منطق را برای خطای دیگر هم اعمال کنید.

پاسخ شماره ۲

مشکل عمیقتر از آن چیزی است که به نظر میرسد. ممکن است شما درک نادرستی از عملکرد دستور return داخل forEach داشته باشید. ساختار کد به گونهای است که فرض شده بررسی کند که آیا هیچ یک از doc.exists برابر با false است و در این صورت زودتر بازگردد، اما به این شکل نوشته شده است، از callback تکرار بازمیگردد.. و چون forEach از مقدار بازگشتی callback استفاده نمیکند، رد شدن promise همچنان بدون رسیدگی باقی میماند.

راههای مناسب برای دستیابی به این نتیجه به شرح زیر خواهد بود:

۱) بهطور مستقیم چیزی را که نیاز دارید بررسی کنید:

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

2) به جای forEach از حلقه for..in یا for..of استفاده کنید:

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

۳) از map استفاده کنید و منتظر نتیجه await بمانید (توصیه نمیشود، زیرا واقعاً به نگاشت نیاز ندارید):

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

توجه داشته باشید که در این حالت هر وعدهای که درون آرایه حاصل رد شود، باید باعث رد شدن وعده بیرونی شود.

توضیح جانبی: شما هرگز نیازی به فراخوانی صریح Promise.reject() ندارید. از آنجایی که تابع شما async است، میتوانید به سادگی چیزی را که به عنوان خطا استفاده میکنید throw کنید - این به هر حال به رد شدن پرامیس تبدیل خواهد شد.