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

Error in Typescript: The identifier 'Proxy' is unknown

I'm trying to create a new variable using the Proxy type from the ES6 specification: myProxy: Proxy; However, I'm encountering the following error: Cannot find name 'Proxy'. Can anyone point me in the right direction to resolve th ...

Could you provide an explanation of the styled() function in TypeScript?

const Flex = styled(Stack, { shouldForwardProp: (prop) => calcShouldForwardProp(prop), })<LayoutProps>(({ center, autoWidth, autoFlex, theme }) => ({ })); This syntax is a bit confusing to me. I understand the functionality of the code, b ...

Encountering a problem in React.js and Typescript involving the spread operator that is causing an error

Could someone assist me with my current predicament? I attempted to work with TypeScript and utilize the useReducer hook. const initialState = { a: "a" }; const [state, dispatch] = useReducer(reducer, {...initialState}); I keep encountering an error ...

Tips for tracking the evolution of changes to an array within React State

Experiencing challenges with saving the history of updates and modifications on a State. I have an object called "Journey" which includes a list of workshops (another array). Whenever I update my list of workshops, I aim to create a new array that captures ...

Simple steps for Mocking an API call (Get Todos) using ComponentDidMount in React with Typescript, Jest, and Enzyme

About the application This application serves as a basic To Do List. It retrieves tasks from an API located at https://jsonplaceholder.typicode.com/todos?&_limit=5. Objective of the project The main goal is to test an API call that triggers ...

Guide on how to show the index value of an array on the console in Angular 2

Is there a way to show the array index value in the console window upon clicking the button inside the carousel component? The console seems to be displaying the index value twice and then redirecting back to the first array index value. Can we make it so ...

Interactive Tab content display

I'm working on a tabs component and I need Angular to only render and initialize the active tab instead of all tabs. Is there a way to achieve this? <my-tabs> <my-tab [tabTitle]="'Tab1'"> <some-component></some-co ...

Disable inline imports when implementing an interface in vscode by selecting the "Implement interface" option

When using TypeScript, if I perform an auto-fix on a class name by selecting "Implement interface", it will generate the methods with inline imports like this: getInbox(): Observable<import('../../model/Message').Interactions[]> { t ...

Issue with Angular 8: discrepancy between the value utilized in component.html and the value stored in component.ts (Azure application service)

Encountering a peculiar behavior in one of my Angular applications. In the component.html file, I aim to display "UAT" and style the Angular mat elements with a vibrant orange color when in UAT mode, while displaying them in blue without any mention of UAT ...

AngularJS Constants in TypeScript using CommonJS modules

Let's talk about a scenario where I need to select a filter object to build. The filters are stored in an array: app.constant("filters", () => <IFilterList>[ (value, label) => <IFilterObject>{ value: value, label: label } ]); i ...

Receiving distinct data from server with Ionic 2 promises

Utilizing ionic 2 RC0 with promises to retrieve data from the server, I am facing an issue where I consistently receive the same data in every request due to the nature of promises. Hence, my question is: How can I tackle this problem and ensure differen ...

Using ngIf for various types of keys within a JavaScript array

concerts: [ { key: "field_concerts_time", lbl: "Date" }, { key: ["field_concert_fromtime", "field_concert_totime"], lbl: "Time", concat: "to" }, { key: "field_concerts_agereq", lbl: "Age R ...

What is the best way to integrate environment-specific configuration options into an AngularJS and Typescript project?

Currently, I am working on a project using AngularJS, Typescript, and VisualStudio. One of the key requirements for this project is to have a configuration file containing constants that control various settings such as REST API URLs and environment names. ...

The tsconfig.json file does not support the path specified as "@types"

Having set up multiple absolute paths for my Next.js application, I encounter an issue where importing a component from the absolute path results in something like "../componentName" instead of "@components/componentName" when I am inside another folder. T ...

Typescript Next.js Project with Custom Link Button Type Definition

I have a project that includes a custom Button component and a NextLink wrapper. I want to merge these two components for organization purposes, but when I combine the props for each, I encounter an issue with spreading the rest in the prop destructuring s ...

Can optional properties or defaults have a specific type requirement defined for them?

When defining a type for a function's input, I want to designate certain properties as required and others as optional: type Input = { // Required: url: string, method: string, // Optional: timeoutMS?: number, maxRedirects?: number, } In ...

"Using TypeScript: How to effectively wait for the completion of the array.sort

Need to implement a loading screen in Angular until an array is sorted. Solution provided below: this.isSorting = true; this.array = this.array.sort((a,b) => a.totlaTime - b.totalTime); this.isSorting = false; The issue faced is when isSorting is set t ...

Using Jimp to load a font and retrieve its Base64 representation

I am currently working in Angular with Jimp, attempting to overlay text onto an existing image. However, the image is not updating as expected. const Jimp = require('jimp') var _this = this; Jimp.read("assets/TimeLine.png").then(function ( ...

Sharing information between sibling modules

Currently, I am faced with the challenge of transmitting data between two sibling components within the following component structure. The goal is to pass data without changing the relationships between these components. I prefer not to alter the componen ...

Ways to prevent scrolling in Angular 6 when no content is available

I am developing an angular 6 application where I have scrollable divs containing: HTML: <button class="lefty paddle" id="left-button"> PREVIOUS </button> <div class="container"> <div class="inner" style="background:red">< ...