How to fix Typescript error “Object is possibly 'undefined'”

守給你的承諾、 提交于 2020-05-24 04:15:31

问题


I'm building a cloud function that will use the Stripe API to process payments. This is within a firebase project. When I run firebase deploy I get the error "Object is possible 'undefined'" const existingSource = customer.sources.data.filter( (s) => s.id === source).pop(); I'm not sure how to resolve this.

Here is my index.ts

import * as functions from 'firebase-functions'; 

export { stripeAttachSource } from './sources';
export { stripeCreateCharge, stripeGetCharges } from './charges';
export const testFunction = functions.https.onCall( async (data, context) => {
    const uid = context.auth && context.auth.uid; 
    const message = data.message; 
    return `${uid} sent a message of ${message}`
}); 

here is my sources.ts

import * as functions from 'firebase-functions';
import { assert, assertUID, catchErrors } from './helpers';
import { stripe } from './config'; 
import { getOrCreateCustomer } from './customers';

/** Attachs a payment source to a stripe customer account.  */
export const attachSource = async(uid: string, source: string) => {

    const customer = await getOrCreateCustomer(uid); 

    const existingSource = customer.sources.data.filter( (s) => s.id === source).pop(); 
    if (existingSource != undefined) {
        let t = existingSource; 
        return t; 
    } else {
        await stripe.customers.createSource(customer.id, { source: source }); 
        //update default
        return await stripe.customers.update(customer.id, { default_source: source}); 
    }
}

///////// DEPLOYABLE FUNCTIONS //////////////////////////////////
export const stripeAttachSource = functions.https.onCall( async ( data, context) => {
    const uid = assertUID(context); 
    const source = assert(data, 'source'); 

    return catchErrors(attachSource(uid, source));  
})

Here is customers.ts where getorCreateCustomer exists

import { assert } from './helpers'; 
import {db, stripe } from './config'; 


/** 
Read the user document from Firestore
 */
export const getUser = async(uid: string) => {
    return await db.collection('users').doc(uid).get().then(doc => doc.data()); 

}

/**
 * Gets a customer from Stripe
 */
export const getCustomer = async(uid: string) => {
    const user = await getUser(uid); 
    return assert(user, 'stripeCustomerId'); 
}

/**
Updates the user document non-desctructively
 */
export const updateUser = async(uid: string, data: Object) => {
    return await db.collection('s-users').doc(uid).set(data, { merge: true }); 
}

/**
Takes a Firebase user and creates a Stripe customer account
 */
export const createCustomer = async(uid: any) => {
    const customer = await stripe.customers.create({
        metadata: { firebaseUID: uid }
    })
    await updateUser(uid, {stripeCustomerId: customer.id })

    return customer; 
}

/** Read the stripe customer ID from firestore, or create a new one if missing */
export const getOrCreateCustomer = async(uid: string) => {
    const user = await getUser(uid); 
    const customerId = user && user.stripeCustomerId; 

    //if missing customerId, create it
    if (!customerId) {
        return createCustomer(uid); 
    }
    else {
        return stripe.customers.retrieve(customerId); 
    }
}


回答1:


Based on the definitions and contents of your functions, TypeScript is unable to infer the return type of getOrCreateCustomer. It is making the assumption that it could return undefined, and its strict mode is calling you out on the fact that you could be referencing a property on an undefined object, which would result in an error at runtime.

What you need to do is declare the return type to be something that can't possibly be undefined, and make sure the code in the body of the function is correct on that guarantee (otherwise you'll get a new error).

If you can't do that (but you really should do that), you might want to instead disable strict mode in your tsconfig.json file, since that is what's enforcing this level of correctness in your code.

I suggest the first option, even if you have to write more lines of code, as it's a better use of TypeScript's typing system.




回答2:


What @Doug mentioned, but also you could write your logic to make sure that every part of customer.sources.data is not undefined...

ie:

const { sources } = customer

if (sources) {
  const { data } = sources 
  if (data) {
     // then filter / etc etc ...
  }
}



回答3:


7 months later, I figured out the best solution.

I simply wrapped the the contents of the firebase callable function in the following if/else statement. It's a bit redundant but it works.

if (!context.auth) {
    // Throwing an HttpsError so that the client gets the error details.
    throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
        'while authenticated.');
  }
  else{ ...copy function code here }

If you don't care about the authentication piece you can simply define the type of context as any.

(data, context:any)


来源:https://stackoverflow.com/questions/55426385/how-to-fix-typescript-error-object-is-possibly-undefined

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!