Developing a type specifically for this function to operate on tuples of varying lengths

I am currently developing a parser combinator library and I am in need of creating a map function that can take N parsers in a tuple, along with a function that operates on those N arguments and returns a parser that parses into the return type specified by the function.

<A, B, Z>(ps: [a: Parser<A>, b: Parser<B>], f: (a: A, b: B) => Z): Parser<Z>
<A, B, C, Z>(ps: [a: Parser<A>, b: Parser<B>, c: Parser<C>], f: (a: A, b: B, c: C) => Z): Parser<Z>
// etc

I am exploring ways to define the type of the map function so it can handle any number of parsers.

An implementation of this function is already in place, except for defining the specific types.

To experiment with this concept, here's a minimal reproduction:

type Parser<T> = () => T;

const string: Parser<string> = null as any;
const number: Parser<number> = null as any;

type MapN = {
    <A, B, Z>(ps: [a: Parser<A>, b: Parser<B>], f: (a: A, b: B) => Z): Parser<Z>,
    <A, B, C, Z>(ps: [a: Parser<A>, b: Parser<B>, c: Parser<C>], f: (a: A, b: B, c: C) => Z): Parser<Z>,
}

const mapN: MapN = null as any;

const p1 = mapN([string, number], (a, b) => [a, b] as const);
const p2 = mapN([string, number, number], (a, b, c) => [a, b, c] as const);
// const p3 = mapN([string, number, string, string], (a, b, c, d) => [a, b, c, d] as const);
// const p4 = mapN([string, number, string, number, number], (a, b, c, d, e) => [a, b, c, d, e] as const);

Is there a way to define this function to work with any number of arguments while maintaining type safety?

Playground

Answer №1

Achieving this can be done through recursive tuple iteration.

The process involves iterating through P, which is the tuple of parsers, and deducing one element at a time (refer to line

P extends readonly [Parser<infer R>, ...(infer Tail)]
).

Subsequently, we pass this information back into the second result parameter T of ExtractParserReturnTuple, continuing until we reach the base case of only one element left in the input array P, as indicated by

P extends readonly [Parser<infer R>]
; it is at this point that we incorporate R into the result tuple T.

This approach is suitable for handling any number of parameters. However, it is necessary to include an 'as const' declaration both for the input parsers and the return type of your inner function. This requirement arises due to limitations associated with how 'readonly' operates - perhaps there might be alternative methods that I am not yet familiar with.

type Parser<T> = () => T;

const string: Parser<string> = null as any;
const number: Parser<number> = null as any;

type ExtractParserReturnTuple<P extends readonly Parser<any>[], T extends any[] = []> = 
    P extends readonly [Parser<infer R>]
        ? readonly [...T, R]
    : P extends readonly [Parser<infer R>, ...(infer Tail)]
        ? Tail extends readonly Parser<any>[]
            ? ExtractParserReturnTuple<Tail, [...T, R]>
            : readonly []
        : readonly [];


function mapN<P extends readonly Parser<any>[], R>(parsers: P, func: (...args: ExtractParserReturnTuple<P>) => R): R {
    return null as unknown as R;
}

const p1 = mapN([string, number] as const, (a, b) => [a, b] as const);
const p2 = mapN([string, number, number] as const, (a, b, c) => [a, b, c] as const);
const p3 = mapN([string, number, string, string] as const, (a, b, c, d) => [a, b, c, d] as const);
const p4 = mapN([string, number, string, number, number] as const, (a, b, c, d, e) => [a, b, c, d, e] as const);

Playground link

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

The keys within a TypeScript partial object are defined with strict typing

Currently, I am utilizing Mui components along with TypeScript for creating a helper function that can generate extended variants. import { ButtonProps, ButtonPropsSizeOverrides } from "@mui/material"; declare module "@mui/material/Button&q ...

The functionality of the disabled button is malfunctioning in the Angular 6 framework

Just starting out with angular 6 and I'm using formControl instead of FormGroup for business reasons. app.component.html <button class="col-sm-12" [disabled]="comittee_Member_Name.invalid && comittee_Member_Number.invalid && c ...

Distribute among an array of specific types

I am trying to achieve this behavior using Typescript: type animals = 'cat' | 'dog' let selectedAnimals: animals[] = ['cat'] selectedAnimals = [ // <- Type 'string[]' is not assignable to type 'animals[]&ap ...

Angular 2 Popup Modal Issue: "Expression modified after checking"

See the problem in action on YouTube Check out the GitHub repository for the demo app My simple app consists of an app component, a child component (account), and an alert service that handles a message dialog component (popup modal). To demonstrate the ...

Deactivate the rows within an Office UI Fabric React DetailsList

I've been attempting to selectively disable mouse click events on specific rows within an OUIF DetailsList, but I'm facing some challenges. I initially tried overriding the onRenderRow function and setting CheckboxVisibility to none, but the row ...

What properties are missing from Three.js Object3D - isMesh, Material, and Geometry?

Currently, I am working with three.js version r97 and Angular 7. While I can successfully run and serve the application locally, I encounter type errors when attempting to build for production. Error TS2339: Property 'isMesh' does not exist on ...

Is there a more effective method to return a response apart from using a redundant function?

function unnecessaryFunction(){ let details: SignInDetails = { user: user, account: account, company: company }; return details; } I am being told that the details value is unnecessary. Is there ...

Navigating the correct way to filter JSON data using HttpClient in Angular 4

Struggling with transitioning from Http to HttpClient, here's the code in question: constructor(public navCtrl: NavController, public http: HttpClient, private alertCtrl: AlertController) { this.http.get('http://example.com/date.php') .su ...

I will not be accessing the function inside the .on("click") event handler

Can someone help me troubleshoot why my code is not entering the on click function as expected? What am I missing here? let allDivsOnTheRightPane = rightPane.contents().find(".x-panel-body-noheader > div"); //adjust height of expanded divs after addi ...

Constructor polymorphism in TypeScript allows for the creation of multiple constructor signatures

Consider this straightforward hierarchy of classes : class Vehicle {} class Car extends Vehicle { wheels: Number constructor(wheels: Number) { super() this.wheels = wheels } } I am looking to store a constructor type that ext ...

Encountered an issue with locating the module 'webpack-cli/bin/config-yargs' while attempting to run webpack-dev

Encountering an error while trying to start the webpack dev server with the command provided below. Despite suggestions that it could be due to outdated webpack versions, I am confident that all components are up to date: [email protected] [email ...

Escape from the abyss of callback hell by leveraging the power of Angular, HttpClient, and

I'm currently grappling with understanding Angular (2+), the HttpClient, and Observables. I'm familiar with promises and async/await, and I'm trying to achieve a similar functionality in Angular. //(...) Here's some example code showca ...

Switching between various components based on conditions within the same route in Angular

My goal is to have 2 separate views, one for the homepage and another for authentication. I want to display the LoginComponent on the route '/' and the SignupComponent on the route '/signup' if the user is not logged in, otherwise rende ...

Attempting to connect to "http://localhost:4242/webhook" was unsuccessful due to a connection refusal when trying to dial tcp 127.0.0.1:4242

In my next.js 13 project, I am integrating stripe with TypeScript and configuring the app router. To create a stripe event in my local machine, I ran stripe listen --forward-to localhost:4242/webhook, but now I am encountered with the error message stripe ...

The limitations of Typescript types influence the program's behavior

As a newcomer to the Typescript environment, I am currently developing a test application to familiarize myself with it. However, I have encountered an issue regarding type restrictions that seems to be not working as expected. In my class, I have defined ...

Issue with rest operator behavior in TypeScript when targeting es2018

This specific code snippet functions properly in the TypeScript Playground... class Foo { constructor(...args: any[]) { } static make(...args: any[]): Foo { return new Foo(...args); } } Example However, when trying to incorpora ...

Encountering the "RequestDevice() chooser has been cancelled by the user" error when using Electron 17.x with Web Bluetooth

After reviewing the following StackOverflow resources: Web Bluetooth & Chrome Extension: User cancelled the requestDevice() chooser Electron Web Bluetooth API requestDevice() Error Can you manipulate web bluetooth chooser that shows after calling requestD ...

Errors in Compiling Dependencies for d3.js Using Typescript

Currently, I am in the process of developing a web application utilizing Node.js alongside Angular, Typescript, and d3.js, among other technologies. The application is functioning properly with library features working as expected. However, I am encounteri ...

Incorporating AngularFire2 in the latest Angular 11.0.5 framework

I have been attempting to utilize Firebase as a database for my angular application. Following the guidance provided in these instructions (located on the official developers Github page), I first installed npm install angularfire2 firebase --save in my p ...

Implementing unique union type in React: Effective ways to declare typescript type for prop value

I am currently facing an issue where I need to set a specific type for a prop value. However, the challenge lies in the fact that the types are string unions which can vary depending on the usage of the React Component. Let me provide you with the TypeScr ...