Creating a universal wrapper function to serve as a logging tool?

Currently, I am working on a generic JS function that can wrap any other function. The purpose of this wrapper is to execute the wrapped function, log the input and output events, and then return the output for "transparent" logging. However, as I attempt to transition this function to TypeScript while maintaining type information for both the wrapped function and its output, I am facing some complexity.

Here is my current progress:

const syncLogger = <T>(f: T) => (...args: unknown[]): ReturnType<T> => {
  let value;
  try {
    value = f(...args);
    functionLogger('info', f, value, ...args); //logging
  } catch (error) {
    functionLogger('error', f, error.message, ...args); //logging
    throw error;
  }
  return value;
};

and here is how it should be utilized:

const myLoggedFunction = syncLogger(originalFunction);

The main issue arises with the args, which are used as the input for functions. I am struggling to find a way to inform TypeScript that these arguments correspond precisely to the parameters of the original function being wrapped.

Answer №1

For a more versatile approach, I suggest making the syncLogger function generic in both the arguments tuple A and the return type R:

const syncLogger = <A extends any[], R>(f: (...a: A) => R) => (
  ...args: A
): R => {
  let value;
  try {
    value = f(...args);
    functionLogger("info", f, value, ...args); // actual logging
  } catch (error) {
    functionLogger("error", f, error.message, ...args); //actual logging
    throw error;
  }
  return value;
};

This modification allows for expected behavior:

function originalFunction(x: string, y: number): boolean {
  return (x <= y.toFixed())
}

const myLoggedFunction = syncLogger(originalFunction); // works fine
// const myLoggedFunction: (x: string, y: number) => boolean

const bool = myLoggedFunction("121", 123); // logs info: originalFunction, true, "121", 123
console.log(bool) // prints: true

In addition, TypeScript's support for higher-order type inference from generic functions starting with version 3.4 enables successful use cases even when the original function is generic:

const loggingItself = syncLogger(syncLogger);
// Prior to TS 3.4: 😢
// const loggingItself: (...args: any[]) => (...args: any[]) => {} 
// From TS 3.4 onwards: 😊
// const loggingItself: <A extends any[], R>(f: (...a: A) => R) => (...args: A) => R

I hope this information proves helpful. All the best!

Link to 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

Tips for displaying a multi-select dropdown in the Creative Tim Angular Material Pro filter panel

I am in need of assistance with modifying the standard Creative Tim Angular Pro Material template due to my limited CSS/SCSS skills. Could someone provide examples of the necessary changes, whether it involves altering the HTML or multiple CSS files withi ...

Ensuring thoroughness in validation without the use of specific text strings

Implementing the assignment or assertion of never at the end of a function is a strategy commonly used in Typescript to ensure exhaustive checks at compile time. To enable the compiler to recognize this, explicit strings are needed for it to check against ...

Sending a parameter between files in a React application: a step-by-step guide

I am currently working on a Pokedex website where I have Pokemon cards displaying data from a JSON file. When a user clicks on a card, a modal view appears with more detailed information about that specific card. I need help in ensuring that only the deta ...

Is there a way to conceal an element within a component based on the current component being used with the router?

I have managed to hide an entire component, but I am unsure of how to show or hide specific elements within a component. export class AppComponent { headerFooterVisible: boolean; constructor(private router: Router) { router.events.subscribe(e =&g ...

Exploring the possibilities of developing WebComponents within Angular using TypeScript

My Angular application was originally created using the default method with ng new project-name". However, for performance reasons, I had to incorporate single standard WebComponents. The JavaScript code associated with these components is stored in a ...

Tips for uploading images, like photos, to an iOS application using Appium

I am a beginner in the world of appium automation. Currently, I am attempting to automate an iOS native app using the following stack: appium-webdriverio-javascript-jasmine. Here is some information about my environment: Appium Desktop APP version (or ...

`transpilePackages` in Next.js causing Webpack issue when used with Styled Components

I'm encountering an issue while utilizing components from a custom UI library in a repository. Both the repository and the web app share the same stack (React, Typescript, Styled Components) with Next.js being used for the web app. Upon running npm ru ...

Unable to load Angular 2 Tour of Heroes application due to Typescript issue

My Angular 2 Tour of Heroes app seems to be stuck on the "Loading..." screen and I can't seem to figure out why. The angular-cli isn't showing any errors either. I'm currently at part five of the tutorial and it's becoming quite frustra ...

Setting a TypeScript version in Atom: Step-by-step guide

I'm currently grappling with using a specific version of TypeScript in Atom. For an older project that relies on Backbone, the latest TypeScript version doesn't compile properly, so I need to use an earlier one. The closest solution I've co ...

The Rxjs `of` operator fails to emit a value when utilizing proxies

I ran into an issue with a basic unit test that utilizes Substitute.js (I also tried using TypeMoq mocks, but encountered the same behavior). My test simply tries to use the of operator to emit the mocked object. Surprisingly, without any additional opera ...

Challenges encountered when using promises for handling mysql query results

I've been working on creating a function that will return the value of a mysql query using promises. Here's what I have so far: query(query: string): string { var response = "No response..."; var sendRequest = (query:string): Prom ...

Validate the data type based on the property

I have a CARD type that can be either a TEXT_CARD or an IMAGE_CARD: declare type TEXT_CARD = { type: "paragraph" | "h1" | "h2"; text: string; }; declare type IMAGE_CARD = { type: "img"; src: string; orient ...

Clear out the existing elements in the array and replace them with fresh values

Within my Progressive Web App, I am utilizing HTTP requests to populate flip cards with responses. The content of the requests relies on the selected values. An issue arises when I choose an item from the dropdown menu. It triggers a request and displays ...

Creating a null array of a specific size can easily be accomplished in Typescript

When I use the splice method to add elements to an array at a specified index, I find myself creating a null array first in order to achieve this. If I use an empty array instead, the elements do not get pushed to the specific instance that I intended. Cur ...

Determining the return type of a function by analyzing its parameters

Let's consider the following scenario: export function bar(bar?: string) { return bar ? { bar } : {}; } const B1 = bar(); const B2 = bar("z"); Upon compilation, the types inferred for both B1 and B2 are: { bar: string; } | { bar? ...

The 'admin' attribute is not found in the 'Object' data type

I have been facing this issue for quite some time now. The backend API response is indicating that a certain property does not exist, even though it clearly does. My Angular application suddenly started showing 18 errors today, and I am at a loss on how ...

When utilizing a personalized Typescript Declaration File, encountering the error message 'Unable to resolve symbol (...)'

I'm having trouble creating a custom TypeScript declaration file for my JavaScript library. Here is a simplified version of the code: App.ts: /// <reference path="types.d.ts" /> MyMethods.doSomething() // error: Cannot resolve symbol "MyMetho ...

Incorporate the Input() component into your codebase and take advantage of its dot notation features, such as

Many Angular directives utilize dot notation options: style.padding.px style.padding.% attr.src In addition, libraries like flex-layout employ this for various responsive sizes: fxLayout.gt-sm fxAlign.sm Can the same concept be applied to a component&a ...

Typescript is throwing a fit over namespaces

My development environment consists of node v6.8.0, TypeScript v2.0.3, gulp v3.9.1, and gulp-typescript v3.0.2. However, I encounter an error when building with gulp. Below is the code snippet that is causing the issue: /// <reference path="../_all.d. ...

Utilize generics to define the data type of the output

Within my Angular service, there is a method that retrieves data from Sync Storage: getFromSyncStorage(key: string): Promise<Object | LastErrorType> { return new Promise(function (resolve, reject) { chrome.storage.sync.get(key, function ( ...