Function 'Once' in Typescript with Generics

Currently, I am utilizing a feature called Once() from FP. In TypeScript, I need to define the types for this function but have been struggling with the implementation. Here's what I have attempted so far:

const once = <T, U>(fn: (arg: T) => U): () => U | undefined => {
let done = false;
return function(this: (args: T) => U) {
    return done ? void 0 : ((done = true), fn.apply(this, arguments));
};

}

I am applying this within a class method:

class CSVOutput extends LogOutput {
private print_headers: (logStat: IStats) => void;
constructor() {
    super();
    this.print_headers = once((logStat: IStats) => {
        console.log(Object.keys(logStat).join(','));
    });
}
public print(logStat: IStats) {
    this.print_headers(logStat);
    console.log(Object.values(logStat).join(','));
}

}

In my tsconfig.json, the configuration appears as follows:

{
"compilerOptions": {
    "esModuleInterop": true,
    "lib": ["es6", "es7", "es2017"],
    "module": "commonjs",
    "sourceMap": true,
    "target": "es6",
    "types": ["node", "mocha"],
    "allowJs": true,
    "outDir": "./dist",
    "preserveSymlinks": true,
    "strict": true,
    "baseUrl": "."
}

}

A warning in TypeScript is triggered stating

Argument of type 'IArguments' is not assignable to parameter of type '[T]'.

Can someone guide me on correctly defining this function in TypeScript?

Answer №1

If I were to modify it, I would go for something in this fashion:

const once = <A extends any[], R, T>(
  fn: (this: T, ...args: A) => R
): ((this: T, ...args: A) => R | undefined) => {
  let executed = false;
  return function (this: T, ...arguments: A) {
    return executed ? void 0 : ((executed = true), fn.apply(this, arguments));
  };
};

In this implementation, we are open to all kinds of functions, including those that expect a specific context for 'this'. The usage of rest tuple types to represent argument lists is consistent throughout. TypeScript does not impose strong typing on 'arguments' as the preference is to use rest parameters with ES2015+. The returned function from 'once()' will have the same context and parameters as 'fn', along with the return type or 'undefined'. It should mostly suit your needs like so:

function shout(x: string) {
  return x.toUpperCase() + "!!!!!!!!!";
}

const shoutOnce = once(shout);
// const yellOnce: (this: unknown, x: string) => string | undefined

console.log(shoutOnce("hello")); // HELLO!!!!!!!!!;
console.log(shoutOnce("goodbye")); undefined

Hoping this explanation proves helpful. Best of luck!

Original code source

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

TypeScript's attempt to replicate Scala's underscore feature has been implemented, but it proves to

I've been working on a personal project for the past 2 years trying to implement Scala's underscore in TypeScript, but haven't been successful. Here is my attempted implementation and its effect. The only thing that I really care about typi ...

The TypeScript compiler encounters difficulties in locating type definitions when utilizing an inherited tsconfig file

There seems to be an issue with the functionality of tsconfig.json inheritance, specifically regarding the proper inheritance of the "typeRoots" setting. http://www.typescriptlang.org/docs/handbook/tsconfig-json.html (folder structure provided below) we ...

Discovering the optimum route within a 2D array given specific limitations using Dynamic Programming

Hey, I have a query related to dynamic programming (dp) that goes like this: Input: A 2D array of numbers Output: The maximum sum of a path from (0,0) to (n-1,n-1) with the following conditions: You can only move down and right i.e. from (A[i-1][j]) t ...

Shuffle and Place indented list

I have a bunch of ideas and a list of projects. I need to choose one idea and match it with a project. I followed this guide to implement the drag and drop feature, but encountered an issue where every project gets assigned the same idea when dragging and ...

Enhancing User Experience through 'Remember Me' Feature in Angular App

I need assistance with adding the Remember Me functionality to a login form in an Angular application. Could someone please provide guidance on how to achieve this? Your help would be highly appreciated! Thank you in advance! Below is my login.ts file: ngO ...

When accessing the innerHTML and outerHTML properties on an HTMLElement, they may return undefined without triggering

Task: My goal is to take an HTML string, make changes to certain attributes of image tags within it, and then return the modified HTML string. The function I have developed follows these steps: private resolveImagesInHTML (body: string): string { le ...

Is it possible to silence the other person's audio using livekit?

As a developer, I create meeting apps using React and livekit technology. I am looking for a reliable method to silence the audio of other participants in the virtual room. Any suggestions? ...

Use the IONIC CAPACITOR application to send a 20-byte command to a BLE device

Hello everyone, I am currently working on an application that is designed to connect to a BLE device. I have a requirement to write 20 Bytes to the device using BleClient.write function. 34h 01h 50h 4Fh 4Ch 49h 54h 45h 43h 00 00 00h 00h 00h 00h 00h 00h 00h ...

Angular File Upload Button Tutorial

English is not my first language, so please excuse any mistakes. I recently started learning Angular and I'm attempting to build a file upload button that lets users upload files based on dropdown menu options (such as USA States). Once uploaded, the ...

The function switchMap does not exist in this context

After completing the Angular Tour of Heroes tutorial and some others, I decided to start building apps with Angular 2. One important thing I learned is that when we're listening for changes with a Subject, it's good practice to wait for a few sec ...

How can I retrieve a global variable in Angular that was initialized within an IIFE?

I'm a beginner in Angular, so I ask for your patience. Currently, I am in the process of migrating an app from Asp.net MVC5 to Angular. One of the key functionalities of this application involves connecting to a third-party system by downloading a Jav ...

Angular 4 - Seeking clarification on the usage of *ngComponentOutlet

When using *ngComponentOutlet, the following code snippets are employed to handle the displaying: Below is a snippet of functional code: this.displayComponent({ 'objects':[ {component: ToDisplayAComponent, expanded: fals ...

Having issues with Angular material autocomplete feature - not functioning as expected, and no error

I have set up my autocomplete feature, and there are no error messages. However, when I type something in the input field, nothing happens - it seems like there is no action being triggered, and nothing appears in the console. Here is the HTML code: ...

Modifying the value of a property in an object array created using the map method is ineffective

I have a collection of objects: https://i.sstatic.net/XNrcU.png Within the collection, I wished to include an additional property to the objects. To achieve this, I utilized the map function: returnArray = returnArray.map((obj) => { obj.active = "fal ...

What are the steps to resolve the error message "Make sure the component is enclosed within a <Provider>" while using Next 14 with Redux Toolkit?

I've been struggling to get Next.js 14 to work with Redux Provider. I followed all the guidelines on the Redux official documentation here, but I keep encountering this error: Error: could not find react-redux context value; please ensure the compo ...

Tips for Customizing the Width of Your Material UI Alert Bar

At the moment, I have refrained from using any additional styling or .css files on my webpage. The width of the Alert element currently spans across the entire page. Despite my attempts to specify the width in the code snippet below, no noticeable change ...

Vue version 3 is encountering an issue with a module that does not have an exported member in the specified path of "../../node_modules/vue/dist/vue"

After I updated my npm packages, errors started popping up in some of the imports from the 'vue' module: TS2305: Module '"../../node_modules/vue/dist/vue"' has no exported member 'X' The X instances affected inclu ...

React Native component that enables users to both input their own text and select options from a dropdown menu as they type

Looking to develop a component that combines Text Input and FlatList to capture user input from both manual entry and a dropdown list. Has anyone successfully created a similar setup? I'm facing challenges in making it happen. Here's the scenari ...

Efficient method to access two arrays simultaneously and combine them into an associative array in JavaScript

When using Ajax to return a table, you have the option of separating column names and row values. Here are two ways to do it: let columns = ["col1", "col2", "col3"]; let rows = [ ["row 1 col 1", "row 1 col 2", "row 1 col 3"] , ["row 2 col 1", "r ...

Tips for efficiently constructing a Docker container using nodejs and TypeScript

Struggling for the past couple of days to get the project running in production, but it keeps throwing different errors. The most recent one (hopefully the last!) is: > rimraf dist && tsc -p tsconfig.build.json tsc-watch/test/fixtures/failing.t ...