问题
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