Creating a dynamic TypeScript signature that includes an optional argument

For some unknown reason, I am attempting to implement a reduce method on a subclass of Map:

const nah = Symbol('not-an-arg');

class MapArray<A, B> extends Map<A, B> {

    reduce<T = [A, B]>(f: (prev: T, next: [A, B]) => any, initialVal?: T) {

        let prev = arguments.length > 1 ? arguments[1] : nah;

        for (const [k, v] of this) {

            if (prev === nah) {
                prev = [k, v];
                continue;
            }

            prev = f(prev, [k, v]);

        }

        return prev;

    }

}

While it may not be the most elegant solution, it does work. However, the issue lies with the typing. Consider the following scenarios:

new MapArray<string,boolean>().reduce((a,b) => 'whatever');

Versus:

new MapArray<string,boolean>().reduce((a,b) => 'whatever', []);

I want the method to capture the type of the second argument [].

In my attempt with TypeScript, I tried something like this using an invalid version:

 type X = (if initialVal ? typeof initialVal : [A, B])
 reduce<R = any, T = X>(f: (prev: T, next: [A, B]) => R, initialVal?: T);

Clearly, this is incorrect but the intention was to capture the type of initialVal if it's provided, otherwise default to [A,B]. Is there a way to achieve this?

See the problem in action at: TS playground

During execution, the error "a is not iterable" occurs when a is assigned a value of 55.

Answer №1

One suggestion is to utilize the undefined keyword instead of nah and implement method overloading:

class MapArray<A, B> extends Map<A, B> {

    reduce(f: (prev: [A, B], next: [A, B]) => any): [A, B];
    reduce<T>(f: (prev: T, next: [A, B]) => any, initialVal: T): T;

    reduce<T>(f: (prev: T | [A, B], next: [A, B]) => any, initialVal?: T) {

        let prev: ([A, B] | T | undefined) = initialVal;
        let index = 0;

        for (const [k, v] of this) {

            if (prev === undefined) {
                prev = [k, v];
                continue;
            }

            prev = f(prev, [k, v]);

        }

        return prev;

    }

}

new MapArray<string,boolean>().reduce((a,b) => 'whatever');
new MapArray<string,boolean>().reduce((a,b) => 'whatever', []);

Interactive playground available here

It seems that this approach aligns closely with @Alexander Mills' solution.

The key thing to note is that the return type of the function f should match that of prev. In your example, using the returned value as a string like "whatever" may not fit seamlessly into the function.

Answer №2

While this response doesn't fully address the original inquiry, it may offer some guidance to others.

One approach could involve utilizing overloads in the following manner:

reduce<R = any>(f: (prev: [A, B], next: [A, B]) => R): R;
reduce<R = any, T = any>(f: (prev: T, next: [A, B]) => R, initialVal?: T) {...}

The limitation here is that I am aiming to extract the user-defined type of initial value instead of necessitating its declaration within the generic parameters.

In essence, I am exploring a solution along the lines of:

reduce<R = any>(f: (prev: [A, B], next: [A, B]) => R): R;
reduce<R = any, T = typeof initialVal>(f: (prev: T, next: [A, B]) => R, initialVal?: <T>) {...}

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

Encountering an Error: Unforeseen Token < Causing Webpack Configuration Problem in Typescript

Seeking assistance to resolve an issue that has arisen while working on a project with Typescript, React, and Webpack. I referred to the guide available at https://www.typescriptlang.org/docs/handbook/react-&-webpack.html After configuring everything, ...

What is the role of the "prepare" function in AWS CDK constructs?

TL;DR: What is the role and purpose of the prepare(): void method in AWS CDK's Construct class? When and how should it be utilized or avoided? The information provided about prepare() states: prepare() function is called after child constructs have ...

Converting a JSON array into a TypeScript array

Looking to convert a JSON array into a TypeScript variable array. The JSON data retrieved from http://www.example.com/select.php: { "User":[ {"Name":"Luca M","ID":"1"}, {"Name":"Tim S","ID":"2"}, {"Name":"Lucas W","ID":"3"} ...

A more concise validation function for mandatory fields

When working on an HTML application with TypeScript, I encountered a situation where I needed to build an error message for a form that had several required fields. In my TypeScript file, I created a function called hasErrors() which checks each field and ...

Compiled TypeScript files are missing require statements for imported components

Recently delved into Angular 2 and encountered an unusual issue. I kicked off using the Angular 2 Quickstart repository on GitHub and incorporated some components with templates. For example: import { Component } from '@angular/core'; import { ...

When initiating an Ionic project, you may notice a repeated message in the terminal saying, "[INFO] Waiting for connectivity with npm..."

I'm in the process of setting up a new Ionic project along with all the necessary dependencies. However, whenever I try to execute the command "ionic serve," I keep getting stuck at the continuous display of the message "[INFO] Waiting for connectivit ...

Is there a way to integrate TypeScript into the CDN version of Vue?

For specific areas of my project, I am utilizing the Vue CDN. I would like to incorporate Typescript support for these sections as well. However, our technical stack limitations prevent us from using Vue CLI. Is there a method to import Vue.js into a bas ...

Enhancing Vue functionality with vue-class-component and Mixins

In my Vue project, I am using vue-class-component along with TypeScript. Within the project, I have a component and a Mixin set up as follows: // MyComp.vue import Component, { mixins } from 'vue-class-component' import MyMixin from './mixi ...

Modify the color of the matSnackbar

I'm having trouble changing the snackbar color in Angular using Angular Material. I tried using panelClass in the ts file and adding it to the global css, but the color remains unchanged. Any suggestions on how to resolve this? I am still new to this ...

TypeScript declaration: discovering modules and defining namespaces

I'm currently in the process of creating a declaration file for h3. For guidance, you can refer to the function available here. Initially, I'm unsure of how typescript identifies declaration files. It seems to detect my declaration when placed ...

How would you define 'Idiomatic' in the context of Idiomatic Javascript?

During his initial discussion on TypeScript, Anders repeatedly mentions the term 'idiomatic javascript'. Can you clarify the specific definition of idiomatic in this context? I've attempted to research this on Wikipedia and Stack Overflow, ...

Jasmine has detected an undefined dependency

Testing out the following code: constructor(drawingService: DrawingService) { super(drawingService); //... } private writeOnCanvas(): void { this.drawingService.clearCanvas(this.drawingService.previewCtx); this.drawing ...

When working with TypeScript, encountering an error involving an array containing an index signature

When attempting to change an object's type to a generic array using the as keyword, I encountered an error. interface TestType { action: TestGeneric<number>[] } type TestGeneric<T> = { [key: string]: T } const func = () => { ...

In Javascript, what significance does the symbol ":" hold?

While exploring the Ionic framework, I came across the following code snippet: import { AlertController } from 'ionic-angular'; export class MyPage { constructor(public alertCtrl: AlertController) { } I'm curious about the significanc ...

`Incorporate concurrent network requests in React for improved performance`

I am looking to fetch time-series data from a rest service, and currently my implementation looks like this async function getTimeSeriesQuery(i) { // Demonstrating the usage of gql appollo.query(getChunkQueryOptions(i)) } var promises = [] for(var i ...

Encountered an issue while attempting to convert a vue-cli project to TypeScript

I am currently attempting to migrate my vue-cli project to typescript. According to this resource, all I need to do is execute the following command: vue add typescript My project is being run on a Windows machine using Git Bash However, when I try to ru ...

I am sorry, but it seems like there is an issue with the definition of global in

I have a requirement to transform an XML String into JSON in order to retrieve user details. The approach I am taking involves utilizing the xml2js library. Here is my TypeScript code: typescript.ts sendXML(){ console.log("Inside sendXML method") ...

Utilize React to display a Selected button within a whitespace

Currently, I am encountering an issue in React where I have a group of buttons at the bottom. Upon selecting a button, the corresponding text should display at the top within a specified whitespace. However, I am looking to have the whitespace already occu ...

Cannot execute npm packages installed globally on Windows 10 machine

After installing typescript and nodemon on my Windows 10 machine using the typical npm install -g [package-name] command, I encountered a problem. When attempting to run them through the terminal, an application selector window would open prompting me to c ...

What is the best way to include the parameter set in the interceptor when making a post request?

-> Initially, I attempt to handle this scenario in the axios request interceptor; if the parameter is uber, then utilize a token. If the parameter is not uber, then do not use a token. -> Afterward, how can I specify uber as a parameter in the custo ...