A guide to verifying the type of response from an HTTP request in Typescript

Issue:

I am currently working with Firebase cloud functions and encountering a specific problem.

Let's consider the following function:

function giveMeAnInteger(): number {
    return 123;
}

When calling this function like so:

function call() {
    const result = giveMeAnInteger();
    console.log(result.split(" "));
}

Typescript is able to statically check the return type and flag an error stating "Property 'split' does not exist on type 'number'".
Now, if we have a similar function published to Firebase's cloud functions:

export const giveMeAnInteger = functions.https.onCall((): number => {
    return 123;
});

Using Typescript to infer the response type when calling the cloud function can lead to potential runtime errors:

const giveMeAnInteger = httpsCallable(functions, "giveMeAnInteger");

export function test() {
    giveMeAnInteger().then((result) => {
        const res = result.data
        console.log(res.split(" "));
    });
}

The error message received is "'res' is of type 'unknown'." due to the signature of httpsCallable: HttpsCallable<unknown, unknown>

Solution Attempt 1:

To address this issue, one option is to declare the return type explicitly before making the function call:

const giveMeAnInteger = httpsCallable<void, number>(functions, "giveMeAnInteger");

export function test() {
    giveMeAnInteger().then((result) => {
        const res = result.data
        console.log(res.split(" "));
    });
}

Although this resolves the typing error, it shifts the burden onto the client to set the correct return type and understand the implementation details of the cloud function.

Solution Attempt 2:

An alternative approach involves hardcoding the return type of the cloud function and exporting it:

export const giveMeAnInteger = functions.https.onCall((): number => {
    return 123;
});

export type ReturnTypeOfGiveMeAnInteger = number;

By importing and utilizing the defined return type, static type checking remains accurate without exposing implementation details:

import { ReturnTypeOfGiveMeAnInteger } from ...

export function test() {
    giveMeAnInteger().then((result) => {
        const res = result.data as ReturnTypeOfGiveMeAnInteger
        console.log(res.split(" "));
    });
}

This method offers a cleaner solution but may still be considered somewhat hacky or hardcoded.

If you have any alternative solutions or additional questions, I would appreciate your input for further discussion.

Answer №1

When it comes to TypeScript, inferring the return type of a callable function statically from the caller's perspective is not possible. The client-side and server-side code operate independently in the eyes of the TypeScript compiler. It lacks insight into how data is serialized and deserialized by the Firebase SDK, leaving room for unexpected outcomes along the way. Therefore, callable functions in TypeScript default to returning unknown, requiring manual casting to achieve the desired type. Unfortunately, there are no shortcuts or workarounds within pure TypeScript to simplify this process.

To ensure a more predictable structure and content in the return type, consider using a runtime type schema like zod to validate that the received data aligns with your backend expectations.

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

Using Firebase Database, Angular2 employs UID (User ID) as the primary key

Creating a signup component in my app using Firebase as the authentication method and database. After registering a new user, a key is generated and all user information, including the UID from authentication, is saved in the database. I want to use the UI ...

Encountering issues with Phaser Sprite body when receiving undefined input

I am having trouble with flicking when I click the mouse. The variable on input.on is returning undefined when I click the mouse. Here is a code snippet that explains my issue. Phaser is a new concept to me. w : number; h : number; velocity:n ...

What are the steps for importing KnockOut 4 in TypeScript?

It appears straightforward since the same code functions well in a simple JS file and provides autocompletion for the ko variable's members. Here is the TypeScript code snippet: // both of the following import lines result in: `ko` undefined // impo ...

Incorporate a service into a base class in Angular2 to ensure its functionality extends to all derived classes

I have multiple classes with a hierarchical inheritance structure as follows: class A (an abstract class) class B extends A class C extends B I am looking to incorporate a service into class A to enable a function that utilizes notifications. How can I ...

Is there a way to achieve region collapsing and expanding using #region / #endregion in TypeScript within Visual Studio Community Edition 2019?

For a while now, we've been utilizing "region collapsing" in TypeScript as shown below: //#region GUI handlers ...code related to handling GUI events... //#endregion This method has functioned well in VS2015 CE and VS2017 CE, with a small "-" or "+" ...

Connecting a Database with NestJS and TypeORM: A step-by-step guide to establish a connection with TypeORM and ensure easy access to

Could someone please explain how to create a DB instance using TypeORM? I want it to be accessible like this service, but the Connection class is deprecated. import { Inject, Injectable } from '@nestjs/common'; import { Connection, Repository } ...

Mismatched data types for function arguments

const x: Example = { toY: (y: Maple) => { return y.p; } }; interface Example { toY: (y: Pine) => void; } interface Pine { c: string; } interface Maple extends Pine { p: boolean; } Despite the warning for interface names ...

What's up with the strange event handling in Angular2?

How can a draggable vertical bar be created with Angular2? Setting isDragging to true when the user clicks it and calling moveHandler when the mouse moves seems straightforward, but there are a couple of issues: When the if-condition in ngOnInit is true, ...

how to send both the useState setter and object as props to a child component in React using TypeScript

Having an issue with passing useState setter and object (both together) to the child component. Successfully passed the object by spreading it like this {...object}, but unsure of the syntax to pass the setter along as well. Here's a code example: < ...

Experiencing Problems with Bot Framework Authentication - Receiving HTTP 401 Error

My current project involves creating a chat bot using the Microsoft Bot Framework and SDK in TypeScript. I am working on implementing user authentication for the bot to interact with Azure DevOps on behalf of users. While testing authentication in Azure Po ...

The method takes in an array of user names along with an HTTP request for each, then it will generate an observable array of user objects as output

I need to retrieve an array of user objects from a non-observable array of usernames (string[]). I am looking for a method that can fetch each user object through getUser(username) (HTTP GET request from Angular in-memory web API) for each provided usernam ...

What is the best way to ensure type safety in a Promise using Typescript?

It seems that Promises in Typescript can be type-unsafe. This simple example demonstrates that the resolve function accepts undefined, while Promise.then infers the argument to be non-undefined: function f() { return new Promise<number>((resolve) ...

Retrieve new data upon each screen entry

After running a query and rendering items via the UserList component, I use a button in the UserList to run a mutation for deleting an item. The components are linked, so passing the deleteContact function and using refetch() within it ensures that when a ...

Error message: Invariant Violation: Portal.render() being caused by semantic-ui-react Basic Modal

As part of enhancing an existing React component, I attempted to include a basic modal (link to documentation). Everything was working well without the modal, but once I added it in following the semantic-ui-react guidelines, I encountered a runtime error ...

Creating a one-of-a-kind entry by adding a number in JavaScript

I am looking for a way to automatically add an incrementing number to filenames in my database if the filename already exists. For example, if I try to add a file with the name DOC and it is already present as DOC-1, then the new filename should be DOC-2. ...

What causes the index to display [object Object] rather than an integer in React?

It has been a long time since I last worked with React, and now I'm facing an issue. Whenever I use console.log to display the index within the map function, my console output looks like this: https://i.stack.imgur.com/VbGmE.png However, the result ...

Utilize Immer and ES6 to effortlessly update a nested object within the Redux store by manipulating normalized data structure

I own a store where I use the global Update action I am interested in updating only a section of the data, without having to get the full data again to normalize Here is how my store looks like: { store:{ students:{ "1": { "n ...

Can you explain the meaning of <T = {}>?

While browsing through the documentation, I came across this generic type: type GConstructor<T = {}> = new (...args: any[]) => T; https://www.typescriptlang.org/docs/handbook/mixins.html Above this line, there is a brief mention that it is a Gene ...

Utilizing generics with Swagger in NestJS

export class PaginatedResult<T> { @Expose() @ApiResponseProperty(type: T}) // It's unfortunate that this isn't working because it's a type but being used as a value @Transform(({ obj }) => obj.data.map((data) => new obj.cla ...

Tips for exporting and reusing third-party types in TypeScript

I am facing a challenge with my package, which relies on a 3rd party package API for most of its functions. How can I export the types from the 3rd party package API in my own package? For instance: React uses @types/react to define its types Let's ...