What methods can be used to determine the specific object returned by an API when working with typescript?

When retrieving customer information from Stripe, there are three possible object types that can be returned. Typically, I am interested in obtaining the customer ID.

The code snippet below might not be the most elegant solution to handle this scenario.

Is there a more efficient and cleaner way to achieve this?

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

  if (stripeCustomer instanceof Object && "email" in stripeCustomer) {
    // The customer is of type 'Stripe.Customer'
      stripeCustomerId = stripeCustomer.id;
  } else if (typeof stripeCustomer === 'string') { 
    // The customer is an ID
    stripeCustomerId = stripeCustomer;
  } else {
    // The customer is of type 'Stripe.DeletedCustomer'
    stripeCustomerId = null;
  }

Answer №1

If you want to implement custom type-checking logic into the type system, you can utilize user-defined type guards. These guards help in identifying the specified type for an object when certain conditions are met:

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?

}

Answer №2

Your approach is on the right track. By implementing typeguards, you can ensure type-safety and catch potential issues during 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);

To experiment with this code further, check out the TypeScript Playground version of the code.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the TypeScript compiler API to read and make updates to objects is an interesting

I'm delving into the world of the typescript compiler API and it seems like there's something I am overlooking. I am trying to find a way to update a specific object in a .ts file using the compiler API. Current file - some-constant.ts export co ...

Create a Typescript function that adheres to a specified type

Imagine a scenario where a specific type of function is declared within a type type Callback = (err: Error | null, result: any) type UselessFunction = (event: string, context: any, callback: Callback) => void The objective is to declare functions that ...

You are unable to assign to 'total' as it is a constant or a property that cannot be modified

Upon running ng build --prod in my project, I encountered the following error: src\app\components\xxxx\xxxx.component.html(116,100): : Cannot assign to 'total' because it is a constant or a read-only property. The proble ...

Angular select element is not functioning properly with the `addEventListener` method

My current project involves creating a table using the primeng library. The table consists of three rows and three columns, and all the data is static. Even though I am utilizing an external library, I find myself traversing the DOM directly. <p-table ...

Completing a Promise Chain with the Power of Node, MySQL, and TypeScript

I'm currently utilizing node and typescript. In my setup, there's a controller and a repository. Strangely, I am able to log data everywhere except right after the return from the repository. As a result, the controller doesn't receive any d ...

Issues with Angular 2 and Deserialization of .NET List<T>

I'm encountering issues when trying to deserialize a .NET List into an Angular 2 array. An error keeps popping up: ERROR Error: Cannot find a differ supporting object...NgFor only supports binding to Iterables such as Arrays. I've looked around ...

It seems that an error has occurred: DOMException was thrown because the attempt to run 'importScripts' on 'WorkerGlobalScope' has failed. The script located at 'http://localhost:4200/BlinkCardWasmSDK.js' was unable to load properly

Recently, I attempted to integrate this credit card reader into my Angular application. Despite carefully following all the installation steps and obtaining a valid license key, I encountered the following error: Error during the initialization of the SDK! ...

Incorporating an external HTML template into an AngularJS 1.5 component with the powerful combination of Webpack and Types

I am attempting to incorporate an external html file into my Angular component: import { LoginController } from './login.controller'; import './login.scss'; import './login.html'; class LoginComponent implements ng.IComponen ...

Nested function TypeScript declarations

Currently, I am attempting to define a type for my controller function in (nodejs) similar to the following export const registerUser = asyncWrap(async function(req:Request, res:Response, next:NextFunction) { res.status(200).json({ success: true}); }) ...

Is there a way to obtain a unique response in TestCafe RequestMock?

With Testcafe, I have the capability to simulate the response of a request successfully. I am interested in setting up a caching system for all GET/Ajax requests. The current setup functions properly when the URL is already cached, but it fails to prov ...

The function navigator.canShare() encountered a permissions denial while running in Typescript

Currently, I am in the process of developing an Angular8 PWA and have successfully implemented webshare to share text content. To my excitement, Chrome has now extended its support for file sharing starting from May 2019. However, while attempting to int ...

The functionality of Angular 5 reactive form valueChanges is not functioning correctly

I am currently working with a form inside a service: this.settingsForm = this.formBuilder.group({ names: this.formBuilder.array([]), globalIDs: this.formBuilder.array([]), topics: this.formBuilder.array([]), emails: thi ...

How to display currency input in Angular 2

Is there a way to dynamically format input as USD currency while typing? The input should have 2 decimal places and populate from right to left. For example, if I type 54.60 it should display as $0.05 -> $0.54 -> $5.46 -> $54.60. I found this PLUN ...

"iOS users have reported that notifications from Firebase have mysteriously ceased to

Yesterday evening, I was experimenting with Push Notifications from Firebase on my iOS application and everything was functioning correctly. I successfully sent a notification from a Cloud Function to a specific FCM token. However, this morning, notificat ...

The parameter type '(value: any) => any[]' does not match the expected parameter type 'string[]'

I'm encountering an issue where I can't push a value to a state array due to a TS error. Let me highlight the relevant components where the error occurs. The error specifically lies in the child component, within the handleValue(value => [...v ...

What are some techniques to ensure null checking is enforced for objects that are nested within multiple layers

Currently, I am leveraging redux sagas to fetch data asynchronously from various endpoints using a unified interface: export interface ResponseInfo { data?: any; status: number; headers?: any; subCode?: string; } To ensure that null check ...

Error message: The function `useSession` is not defined within the Next.js

I'm currently working on a next.js project with next-auth, where I have successfully implemented the login functionality. However, I'm facing an issue when trying to use the session to fetch user data on a specific page. Whenever I attempt to use ...

Using PrimeNG checkboxes to bind objects in a datatable

PrimeFaces Checkbox In the code snippet below, my goal is to add objects to an array named selectedComponents in a model-driven form when checkboxes are checked. The object type of item1 is CampaignProductModel, which belongs to an array called selectedC ...

Encountering the "Maximum Update Depth Exceeded" error in React Typescript with hooks

I encountered this error: Uncaught Error: Maximum update depth exceeded. It seems to be related to calling setState multiple times within componentWillUpdate or componentDidUpdate. React limits nested updates to prevent infinite loops. I am unsure of what ...

Switching from a Promise to an Observable using React-Redux and TypeScript

I am struggling to grasp the concept of storing a Promise response into Redux. I believe finding a solution to this issue would greatly benefit me. Currently, I am utilizing a library that returns a Promise, and I wish to save this response - whether it i ...