What techniques can be used to create a partially applied function from another function?

I am looking to provide a JsDoc with type signature for the following JavaScript function:

function addExtraData(f, extra) {
  return (props, ...args) => f({ ...props, extra }, ...args)
}

My attempt at defining the JsDoc is not quite right:

/**
 * @param {(props: Props, ...args: Args) => Result} f
 * @param {Extra} extra
 * @template Extra
 * @template {{ extra: Extra }} Props
 * @template {Array} Args
 * @template Result
 * @returns {(props: Omit<Props, 'extra'>, ...args: Args) => Result}
 */

I have defined Omit as

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>

If this cannot be achieved in a JsDoc, I am open to creating a .d.ts file instead.


update

A refined version that works externally:

/**
 * @template {Object} P
 * @template {Array} A
 * @template R
 * @param {(props: P, ...args: A) => R} f
 * @param {P['extra']} extra
 * @returns {(props: Omit<P, 'extra'>, ...args: A) => R}
 */
export function addExtraData(f, extra) {
  return (props, ...args) => f(Object.assign({}, props, { extra }), ...args)
}

However, there is an error being produced for Object.assign:

Argument of type

Pick<P, Exclude<keyof P, "extra">> & { extra: P["extra"]; }
is not assignable to parameter of type 'P'. [2345]

In my understanding, this should result in P, excluding extra and intersecting it with something containing extra of the correct type.

Answer №1

What do you think about this function?

function provideExtra<Props,Extra,Args extends any[],Result>(
  f: (p: Props & {extra: Extra}, ...args: Args) => Result,
  extra: Extra
) {
  return (props: Props, ...args: Args) => f({ ...props, extra }, ...args)
}

UPDATE: It seems TypeScript doesn't infer Props as expected. You can call it like this:

const f = (props: {prefix: string, extra: string}, suffix: string) => props.prefix + props.extra + suffix;
const g = provideExtra<{prefix: string},string,[string],string>(f,"mid");

I managed to reduce the number of type arguments with some type inferrence:

type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type FirstArg<F> = F extends (p: infer U, ...args: any[]) => any ? U : never;
type Extra<F> = FirstArg<F> extends {extra: infer U} ? U : never;
type OtherProps<F> = Omit<FirstArg<F>,"extra" & keyof FirstArg<F>> & { extra?: never};
type Rest<F> = F extends (p: any, ...args: infer V) => any ? V : never;

function provideExtra<F extends (...args: any[]) => any>(
  f: (p: OtherProps<F> & {extra: Extra<F>}, ...args: Rest<F>) => ReturnType<F>,
  extra: Extra<F>
) {
  return (props: OtherProps<F>, ...args: Rest<F>) => f({ ...props, extra }, ...args)
}

const f = (props: {prefix: string, extra: string}, suffix: string) => props.prefix + props.extra + suffix;
const g = provideExtra<typeof f>(f,"mid");
const t = g({prefix: "prefix"}, "suffix");

If there isn't a simpler solution, adding a type assertion might be necessary, for example:

Omit<P, "extra"> & { extra: P["extra"]; }
should be considered as a P.

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

Angular project hosting causing malfunctions in route parameters

Encountering a problem with route parameters after deploying my website on namecheap hosting. Routes Setup: const routes: Routes = [ { path: 'women', component: ProductlistingComponent }, { path: 'women/:search_1', component: ...

How can you limit a type reference to a specific file in TypeScript?

Currently, I am working on writing universal JavaScript code that can be used in both Node and browser environments. While most of the code works independent of the environment, there are certain parts where different implementations are required based on ...

Differences Between React Prop Types and Typescript in Front-End

I'm considering incorporating TypeScript into my project. Would this render the use of prop-types in React unnecessary? With prop-types, I find myself having to manually define types, but TypeScript would eliminate this step. Am I on the right track? ...

Looking for a way to detect changes in a select menu using Angular?

How can I determine with the openedChange event if there have been any changes to the select box items when the mat select panel is closed or opened? Currently, I am only able to detect if the panel is open or closed. I would like to be able to detect any ...

Typescript: Maximizing efficiency and accuracy

When it comes to developing Angular2 apps using Typescript, what are the essential best practices that we should adhere to? ...

Leverage the generic parameter type inferred from one function to dynamically type other functions

I am in the process of developing an API for displaying a schema graph. Here is a simplified version of what it entails: interface Node { name: string; } type NodeNames<T extends Node[]> = T[number]["name"]; // Union of all node names as strings ...

Reasons why a functional component may not trigger a rerender after a state change using useReducer()

When using react Hooks, specifically useReducer, I found that although the state changes, the functional component does not rerender. Additionally, when trying to open the drawer by pressing a button in the menu, even though the state changes the drawer re ...

Creating a Jsonifiable type that aligns with static interfaces: A step-by-step guide

When working with Typescript, there are 3 types that share similarities as they are composed of primitive types, maps, and arrays: type Color1 = { [component: string]: number } type Color2 = { green: number red: number blue: number } interface Colo ...

Importing Json in Angular 8: A detailed guide

I recently came across information that you can now directly import JSON in TypeScript 2.9 and made changes to my tsconfig.json file accordingly: { "compileOnSave": false, "compilerOptions": { "baseUrl": "./", "outDir": "./dist/out-tsc", " ...

No error was flagged when the function had the potential to return undefined

getStage may sometimes return undefined without reporting any errors, which could potentially lead to a code crash. const a = Math.random() > 0.4 function getStage(): string { if(a) { return '' } } c ...

Sort the array by the elements in a separate array

Here is a filters array with three values: serviceCode1, serviceCode2, and serviceCode3. ['serviceCode1', 'serviceCode2', 'serviceCode3'] I have another array with approximately 78 records that I want to filter based on the a ...

Erase Typescript Service

To remove a PostOffice from the array based on its ID, you can use a checkbox to select the desired element and then utilize its ID for the delete function. Here is an example: let postOffices = [ {postOfficeID: 15, postCode: '3006&ap ...

Is it possible to optimize the performance of my React and TypeScript project with the help of webpack?

I am working on a massive project that takes 6 to 8 minutes to load when I run npm start. Is there a way to speed up the loading process by first displaying the sign-in page and then loading everything else? ...

Is there a way to mock a "find" call in mockingoose without getting back "undefined"?

I am currently working with mockingoose 2.13.2 and mongoose 5.12.2, leveraging Typescript and jest for testing purposes. Within my test scenario, I am attempting to mock a call to my schema's find method. Here is what I have tried: import mockingoose ...

ESLint detected a promise being returned in a function argument where a void return type was expected

I'm encountering a recurring error whenever I run my ESLint script on multiple routers in my server. The specific error message is as follows: error Promise returned in function argument where a void return was expected @typescript-eslint/no-misuse ...

Organize elements within an array using TypeScript

I have an array that may contain multiple elements: "coachID" : [ "choice1", "choice2" ] If the user selects choice2, I want to rearrange the array like this: "coachID" : [ "choice2", "choice1" ] Similarly, if there are more tha ...

Error TS 2322 - The property 'id' is not present in the object of type '{ id: number'

Just starting out with Angular and TypeScript. I created a model with the same properties but encountered an error and am struggling to find a solution: TS2322: Type '{ id: number; model: string; plate: string; deliveryDate: string; deadline: st ...

I'm having trouble setting a value for an object with a generic type

I am attempting to set a value for the property of an object with generic typing passed into a function. The structure of the object is not known beforehand, and the function receives the property name dynamically as a string argument. TypeScript is genera ...

Locate a class using an interface

Consider the code snippet below: interface FirstInterface {} interface SecondInterface {} interface ThirdInterface {} class TheClass { constructor(howdy: FirstInterface) {} } class Foo implements FirstInterface {} class Bar implements SecondInterface ...

The Aurelia application encounters a "Maximum call stack size exceeded" error while trying to bind FullCalendar

I am currently working on setting up a JQuery plugin (FullCalendar) within my Aurelia application, which is built using TypeScript. I am relatively new to web development and just trying to get a basic example up and running. To start off, I utilized this ...