Broaden the `...args: Parameters<T>` to enable the inclusion of additional optional parameters towards the end

I am currently working on a function that can take a function as input and return an "extended" function.

The returned function should have the same parameters as the original function, with an additional optional parameter at the end.

I have managed to reuse the parameters from the original function in the returned function, but I am struggling to add the extra optional parameter.

For example, consider the following code snippet (the logging scenario is just an example):

const asyncLog = async (log: string) => { /* ... */ };

function addLogging<T extends (...args: Parameters<T>) => ReturnType<T>>(fn: T, log: string) {
  const fnWithLogging = async (...args: Parameters<T>, doLog = true) => {
    if (doLog) await asyncLog(log);
    return fn(...args);
  };
  return fnWithLogging;
}

const baseFn = async (a: number, b: string) => { /* ... */ };
const baseFnWithLogging = addLogging(baseFn, 'some log');
await baseFnWithLogging(1, '2', false);

The above code works, but I want to pass the parameters individually rather than as an array, like baseFnWithLogging(1, '2', false).

If I change

fnWithLogging(...args: Parameters<T>)
to
fnWithLogging(...args: Parameters<T>)
, I encounter TypeScript error 1014
A rest parameter must be last in a parameter list.
.

Is there a way to achieve this functionality? Perhaps something like

extend<Parameters<T>, doLog = true>
?

Answer №1

To implement this functionality, utilize the rest parameter syntax in the function and then utilize function.length to trim the passed arguments to the function and the optional wrapper parameters.

However, there is an important limitation to be aware of. This approach will not function properly if the passed function contains any optional parameters, as function.length will not include them in the count and it can be challenging to retrieve them (unless you parse the function string).

const asyncLog = async (log: string) => { /* ... */ };

function addLogging<T extends (...args: Parameters<T>) => ReturnType<T>>(fn: T, log: string) {
  const fnWithLogging = async (...args: [...Parameters<T>, doLog?: boolean]) => {
    const funcArgs = args.slice(0, fn.length) as Parameters<T>;
    const [doLog = false] = args.slice(fn.length) as [doLog: boolean];
    
    if (doLog) await asyncLog(log);
    return fn(...funcArgs);
  };
  return fnWithLogging;
}

const baseFn = async (a: number, b: string) => { /* ... */ };
const baseFnWithLogging = addLogging(baseFn, 'some log');
baseFnWithLogging(1, '2', false);

Playground

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

Creating applications with Angular2 and TypeScript is possible even without utilizing NPM for development

I've seen all the guides recommend installing npm, but I'm determined to find an alternative method. I found Angular2 files available here, however, none of them are in TypeScript code. What is the best course of action? Do I need Angular2.ts, ...

What is the best way to retrieve entire (selected) objects from a multiselect feature in Angular?

I'm facing an issue with extracting entire objects from a multiselect dropdown that I have included in my angular template. Although I am able to successfully retrieve IDs, I am struggling to fetch the complete object. Instead, in the console, it dis ...

Tips for disentangling code from types in Typescript

Instead of intertwining code and types like the example below: const compar8 : boolean | error = (action: string, n: number) => { switch(action) { case 'greater': return n > 8; case 'less': ...

The page has been updated following a refresh

I'm currently working on developing an Instagram-inspired platform using Angular 6, but I've run into a puzzling issue. When I refresh the page in my home component, everything reloads correctly and displays all my posts as expected. However, if ...

When using ngStyle to bind a variable, the binding variable will be null

Currently, I am attempting to utilize ngStyle to set the background image. <div class="artist-banner fluid-banner-wrapper" [ngStyle]="{'background-image': 'url(../imgs/banner/' + event?.category + '.jpg)' }"> The fun ...

The error message "Property does not exist on type Object from subscribe" indicates that

When using forkJoin to make multiple http calls, I encountered the error error TS2339: Property 'data' does not exist on type 'Object' forkJoin(this.userservice.getUser(), this.userservice.getDashboard()).pipe( map(([userData, dash ...

The specified type '{ songs: any; }' cannot be assigned to the type 'IntrinsicAttributes' in NEXTJS/Typescript

I am currently working on a straightforward script. Below is the index.tsx file for my Next.js application: import type { NextPage } from 'next' import SongsList from '../components/SongsList/SongsList' import { GetStaticProps } from & ...

Establish a default value for ng2-datepicker

Has anyone figured out how to set an initial value for the ng2-datepicker when using it for available date and date expires? I want the initial value of dateAvailable to be today's date and the initial value of dateExpires to be 2099-12-31. <label ...

Adding a dynamic CSS stylesheet at runtime with the power of Angular and Ionic 2

Currently, I am working on creating a bilingual application using Ionic2. The two languages supported are English and Arabic. As part of this project, I have created separate CSS files for each language - english.css and arabic.css. In order to switch be ...

Clickable Angular Material card

I am looking to make a mat-card component clickable by adding a routerlink. Here is my current component structure: <mat-card class="card" > <mat-card-content> <mat-card-title> {{title}}</mat-card-title> &l ...

You may encounter an error stating "Property X does not exist on type 'Vue'" when attempting to access somePropOrMethod on this.$parent or this.$root in your code

When using VueJS with TypeScript, trying to access a property or method using this.$parent.somePropOrMethod or this.$root.somePropOrMethod can lead to a type error stating that Property somePropOrMethod does not exist on type 'Vue' The defined i ...

Upgrade Angular 8 by substituting interconnected filter and order pipelines with custom functions

According to the Angular documentation Filtering and sorting operations can be resource-intensive. When Angular invokes these pipe methods frequently, it can lead to a degraded user experience, especially with even moderately-sized lists. Misuse of filt ...

What methods are available to expedite webpack compilation (or decouple it from server restart)?

My current setup includes the following configurations: import path from 'path' import type {Configuration} from 'webpack' const config: Configuration = { mode: 'development', entry: path.join(__dirname, '../..&apos ...

Using Next.js and TypeScript to Send Props to Dynamically Typed Objects

I am in the process of developing an application using Next.js with TypeScript. I have encountered an error message stating Type 'VoidFunctionComponent<ShirtDetailProps>' is missing the following properties when passing props to a component ...

Strategies for patiently waiting for an object to appear before loading the HTML

After logging into my service, I download data from a REST API, and based on that data, I display certain tabs. However, I am experiencing issues with loading the HTML content once the data has been retrieved. The ngif directive at the beginning of the H ...

Facing issues with integrating Mixpanel with NestJS as the tracking function cannot be located

While utilizing mixpanel node (yarn add mixpanel) in conjunction with NestJS, I have encountered an issue where only the init function is recognized. Despite calling this function, I am unable to invoke the track function and receive the error message: Ty ...

Exploring Vue 3: Crafting a custom plugin using the composition API and enhancing it with Typescript type augmentation

Encountering an issue with displaying plugins properly within <script> and <template> tags on WebStorm. Firstly, let's take a look at my files and configuration: tsconfig.config.json { "extends": "@vue/tsconfig/tsconfig. ...

Implementing HTTP GET and POST requests in Angular 2 allows for the functionality of displaying, adding, and deleting objects within a list

Hey there, I'm completely new to dealing with HTTP and fetching data from the server. I've been scouring through various tutorials, examples, and questions on different platforms, but unfortunately, I haven't been able to find exactly what I ...

Guide to Re-rendering a component inside the +layout.svelte

Can you provide guidance on how to update a component in +layout.svelte whenever the userType changes? I would like to toggle between a login and logout state in my navbar, where the state is dependent on currentUserType. I have a store for currentUserTyp ...

When creating utility classes, is it beneficial to offer a non-mutable API to facilitate their integration with frameworks such as React?

Currently, I am working on enhancing the functionality of my DateWithoutTime class. As part of this process, private fields within the class need to be updated by public methods. this.state.dateWithoutTimeInstance.shiftBySpecificDaysCount({ daysCount: 5, ...