Injecting properties into higher order functions in Typescript allows for the dynamic customization

I'm curious about the process of composing functions that take an object as the sole argument, where each higher order function adds a property. For instance, I have a function that takes a context as input. I would like to wrap this function with a withCookies function and then a withToken function, allowing me to access both cookies and token within the final function.


withCookies(
  withToken(
    (ctx) => {
      console.log(ctx.cookies) // Record<string, string>
      console.log(ctx.token) // string
    }
  )
)

What might be the typescript signature for such a higher order function?

Answer №1

Creating call signatures for the functions withCookies(), withToken(), and a combined function withProps() is certainly possible. However, one challenge you may face is related to type inference in TypeScript. The compiler might struggle with contextually typing the ctx parameter within your innermost function due to the generic nature of the functions involved. This can lead to difficulties in proper type parameter inference, as demonstrated in issues such as microsoft/TypeScript#33042 and microsoft/TypeScript#38872. To overcome this, manual specification of types at certain points in your code may be necessary for the compiler to correctly understand the logic.


To start, defining the interfaces Cookies and Token helps give clarity to the objects being referenced:

interface Cookies {
  cookies: Record<string, string>;
}
interface Token {
  token: string;
}

The withCookies() function should be generic with an object type T, taking a callback function of type (ctx: T & Cookies) => void and returning a function of type (ctx: T) => void:

/* 
declare const withCookies: (
  cb: (ctx: T & Cookies) => void
) => (ctx: T) => void 
*/

Similarly, withToken() follows a comparable structure but substitutes Cookies with Token:

/* 
declare const withToken: (
  cb: (ctx: T & Token) => void
) => (ctx: T) => void 
*/

The similarity in signatures suggests the possibility of a utility function like withProps() that generates these functions. Here's a sample implementation:

const withProps = <U extends object>(u: U) =>
  <T extends object>(cb: (ctx: T & U) => void) =>
    (ctx: T) => cb({ ...ctx, ...u });

By using withProps() with Cookies as a parameter, you create the withCookies function:

const withCookies = withProps<Cookies>(
  { cookies: { a: "hello", b: "goodbye" } }
);

A similar approach applies to generate withToken by calling withProps() with Token:

const withToken = withProps<Token>(
  { token: "token" }
);

You can validate that these functions align with the defined call signatures.


Attempting to use these functions uncovers contextual typing challenges:

const badResult = withCookies(
  withToken(
    (ctx) => {
      console.log(ctx.cookies) // error! Property 'cookies' does not exist on type 'never'
      console.log(ctx.token) // error! Property 'token' does not exist on type 'never'
    }
  )
);
// const badResult: (ctx: never) => void

The inferred types result in errors due to incorrect contextually typed parameters. To address this, explicit annotations or specifying generic type parameters can be used, ensuring correct behavior:

const result = withCookies(
  withToken(
    (ctx: Cookies & Token) => {
      console.log(ctx.cookies)
      console.log(ctx.token)
    }
);
// const result: (ctx: object) => void

const alsoResult = withCookies(
  withToken<Cookies>(
    (ctx) => {
      console.log(ctx.cookies)
      console.log(ctx.token)
    }
  )
);
// const alsoResult: (ctx: object) => void

Both approaches lead to functional output, demonstrating the successful propagation of objects passed into withProps() into subsequent callbacks.


Click here to access the code via Playground link

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

transmit data from Node.js Express to Angular application

I am making a request to an OTP API from my Node.js application. The goal is to pass the response from the OTP API to my Angular app. Here is how the API service looks on Angular: sendOtp(params): Observable<any> { return this.apiService.post(&q ...

Circular reference in Angular/TypeScript

I encountered a circular dependency issue in my Angular project and tried various solutions, including exporting all dependent classes from a "single file" as suggested here. Unfortunately, this approach did not work for me. I then explored other solutions ...

JavaScript Equivalent to C#'s BinaryReader.ReadString() Function

Currently, I am in the process of translating C# code into JavaScript. While transferring multiple datatypes from this file to matching functionalities found in various JavaScript libraries was relatively smooth, there is one specific function that seems t ...

An error occurred in TypeScript when trying to use the useState function with a string type. The ReferenceError indicates that

import React, { FunctionComponent, useState, useEffect } from 'react' const SearchBar: FunctionComponent = () => { const [searchValue, setSearchValue] = useState<string>('') const [isSuggestionOpen, setIsSuggestionO ...

What is the process for creating a class in TypeScript?

Here is an example of the object structure I am working with: { "info" : { "title" : '123}, "details": [ {"desc": "d1"}, {"desc": "d2}] } I am currently in the process of defining ...

A class or another interface is the only type that an interface is allowed to extend

We are currently using typescript version 2.9.2 I encountered an issue while trying to extend the interface DropDownOption. I received the error "error TS2312: An interface may only extend a class or another interface." Is there an alternate approach to ...

In TypeScript, the catch block does not get triggered

I created a custom pipe in Angular that is supposed to format passed parameters to date format. The pipe contains a try-catch block to handle any errors, but surprisingly the catch block never seems to be executed even when an invalid date is passed. impo ...

Is it possible for the binding model of Mat-Checkbox to be optional or null?

Greetings everyone! I am a newcomer to the world of Angular, where I have successfully created a dynamic list of checkboxes. However, I have encountered a challenge when trying to bind selected values from an API to these checkboxes. <div *ngFor="let b ...

Obtaining undefined values for req and resolvedUrl in GetServerSideProps function

In my project, I am currently using next.js version ""next": "^12.1.4"" and node version ""@types/node": "^14.14.6". I have created a function called getServerSideProps with parameters req and resolvedUrl. When the ...

Tips for adjusting card content to fit its size in material ui?

I'm looking to implement Material UI cards in a grid layout, each containing a Highcharts chart as shown in this demo. However, I'm facing an issue where the content does not adjust properly when the card size is fixed. How can I resolve this? A ...

How can I utilize Angular services to transfer a count value to the Component?

I've been working on creating a coin counter for my application by developing a service specifically for counting coins. However, when I tried to use this service in one of my components where the count function is triggered, I encountered some diffic ...

The function is trying to access a property that has not been defined, resulting in

Here is a sample code that illustrates the concept I'm working on. Click here to run this code. An error occurred: "Cannot read property 'myValue' of undefined" class Foo { myValue = 'test123'; boo: Boo; constructor(b ...

Encountering issues with bidirectional data binding functionality

I have integrated a pagination component from ng-bootstrap into a generic component that includes a select dropdown to choose the number of items per page. I triggered an event from this generic component and caught it in the parent component (member-list. ...

Angular 11 is throwing an error stating that the type 'Observable<Object>' is lacking certain properties as required by its type definition

My code is producing the following error: TS2739 (TS) Type 'Observable<Object>' is missing the following properties from type 'WeatherForecast': ID, date, temperatureC, temperatureF, summary I'm puzzled as to why this error ...

Using dangerouslySetInnerHTML in ReactJS can potentially strip away data attributes

After adding a data-test attribute to the a anchor tag within an HTML string and inserting it using dangerouslySetInnerHTML, I noticed that the data attributes are somehow being stripped out. Is there a way to prevent this from happening? These attribute ...

Formatting decimals with dots in Angular using the decimal pipe

When using the Angular(4) decimal pipe, I noticed that dots are shown with numbers that have more than 4 digits. However, when the number has exactly 4 digits, the dot is not displayed. For example: <td>USD {{amount| number: '1.2-2'}} < ...

Turning a JSON string into interpolation within an Angular application

I received a JSON response that looks like this: { someText: "Order in {000} 12pm PST for early shipping"; cutofftime : "10000000" } What is the most effective way to replace the '{000}' with the dynamic value stored in &quo ...

What is the reason behind installing both Typescript and Javascript in Next.js?

After executing the command npx create-next-app --typescript --example with-tailwindcss my_project, my project ends up having this appearance: https://i.stack.imgur.com/yXEFK.png Is there a way to set up Next.js with Typescript and Tailwind CSS without i ...

Exploring the contrasts and practical applications of Virtual Scroll versus Infinite Scroll within the framework of Ionic 3

After thoroughly reviewing the documentation for Ionic 3, I embarked on a quest to discern the disparity between https://ionicframework.com/docs/api/components/virtual-scroll/VirtualScroll/ and https://ionicframework.com/docs/api/components/infinite-scr ...

What is the best way to explain the concept of type indexing in TypeScript using its own keys?

I'm still learning TypeScript, so please bear with me if my question sounds basic. Is there a way to specify the index for this type so that it utilizes its own keys rather than just being an object? export type TypeAbCreationModal = { [index: stri ...