Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to check which object is returned from an API in typescript?

Stripe can return 3 different object types from as the customer. I want the customer id which is what is normally returned.

What I have below is pretty ugly.

Is there a better way to do this?

  const stripeCustomer: string | Stripe.Customer | Stripe.DeletedCustomer = checkoutSession.customer;
  let stripeCustomerId: string;

  if (stripeCustomer instanceof Object && "email" in stripeCustomer) {
    // customer is of type Stripe.Customer
      stripeCustomerId = stripeCustomer.id;
  } else if (typeof stripeCustomer === 'string') { 
    // customer is id
    stripeCustomerId = stripeCustomer;
  } else {
    // customer is of type Stripe.DeletedCustomer
    stripeCustomerId = null;
  }
like image 735
MadMac Avatar asked Sep 11 '25 17:09

MadMac


2 Answers

You can use a user-defined type guards, which let your plug your own custom type-checking logic into the type system.

When they're true for an object, they inform the flow-sensitive typing that the object has the specified type:

function isStripeCustomer(object: any): object is Stripe.Customer {
    return object instanceof Object 
       && "object" in object 
       && object.object === 'customer' 
       && !object.deleted
}

function isStripCustomerID(object: any): object is string {
    return typeof object === "string"
}

function isStripeDeletedCustomer(object: any): object is Stripe.DeletedCustomer {
    return object instanceof Object
       && "object" in object 
       && object.object === 'customer' 
       && object.deleted
}

const customer: string | Stripe.Customer | Stripe.DeletedCustomer = checkoutSession.customer;

let stripeCustomerId: string | null

if (isStripeCustomer(customer)) {
    stripeCustomerId = customer.id
} else if (isStripCustomerID(customer)) { 
    stripeCustomerId = customer;
} else if (isStripeDeletedCustomer(customer) {
    stripeCustomerId = null;
} else {
    // It was none of the known types. Perhaps a new type of result was
    // add in a future version of their API?

}
like image 58
Alexander Avatar answered Sep 14 '25 11:09

Alexander


The basic concept you use is the right one. You can enhance it with typeguards to make it type-safe and allow to identify issues at compile-time:

interface Customer {
    id: string;
}

type CustomerResponse = Customer | string | null;

function isCustomerObject(customer: CustomerResponse): customer is Customer {
    return customer != null && typeof customer === "object" && customer.id != null;
}

function isString(str: CustomerResponse): str is string {
    return typeof str === "string";
}

function identify(obj: Customer | string | null) {
    if (isCustomerObject(obj)) {
        console.log(obj, "is a customer with ID", obj.id);
    } else if (isString(obj)) {
        console.log(obj, "is a string with value", obj);
    } else {
        console.log(obj, "is null");
    }
}

const customerObject: Customer = {
    id: "123"
};


identify(customerObject);
identify("123");
identify(null);

See the same code on the TypeScript Playground.

like image 27
fjc Avatar answered Sep 14 '25 11:09

fjc