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

Incorporate FontAwesome global components into your Vue project using TypeScript

Hey there, I'm a TypeScript newbie and looking to incorporate FontAwesome icons into my Vue 3 App. Here's the setup: Here is my main.ts : import Vue, { createApp } from 'vue'; import './registerServiceWorker'; import { librar ...

Uploading multiple strings to an Amazon S3 bucket using Node.js by piping a string

Suppose I have a simple loop similar to the one shown below: for (const i=0; i<3; i++) { to(`This incrementer is ${i}`) } At the end of the loop, I expect my file to contain: This counter is 0 This counter is 1 This counter is 2 I at ...

Is there a TypeScript rule called "no-function-constructor-with-string-args" that needs an example?

The description provided at this link is concise: Avoid using the Function constructor with a string argument to define the function body This might also apply to the rule missing-optional-annotation: A parameter that comes after one or more optiona ...

Error: Module './App' not found in webpack module

I am encountering the error Uncaught Error: Module not found: Can't resolve ./App' and ./store in client/src. in the console of my local environment when I execute npm start from the client directory. The console is showing 2 errors: ERROR in ...

Utilizing Dynamic Class Names in Angular 7 with Typescript

In the process of developing an Angular 7 application, I have created a form component that is intended to be used for various entities. As part of this, I defined a variable route: {path: ':entity/create', component: FormComponent} While this ...

Setting up gulp-typescript for Angular 2: A comprehensive guide

Right now I am utilizing the tsc compiler with the watch flag in the command prompt. It functions correctly, loading all definition files and successfully compiling each angular2 file. However, it is quite cumbersome to use via the shell window. My object ...

Tips for effectively utilizing the drag and drop feature with the Table Component in Ant Design

Recently, I received a new project from another team, and my client is requesting some changes to the admin page. Specifically, they want to customize the order of data pulled from the database. For instance, they would like to arrange the job positions in ...

The technique for accessing nested key-value pairs in a JSON object within an Angular application

After receiving the response from the backend, I have retrieved a nested hash map structure where one hash map is nested within another: hmap.put(l,hmaps); //hmap within hmap When returning the response to the frontend, I am using the ResponseEntity meth ...

Send information through a form by utilizing a personalized React hook

I'm having difficulty understanding how to implement a hook for submitting a form using fetch. Currently, this is what I have. The component containing the form: const MyForm = (): ReactElement => { const [status, data] = useSubmitForm('h ...

Exploring the process of retrieving data from localStorage in Next.js 13

Having recently delved into the realm of Next JS, I've encountered a hurdle when it comes to creating middleware within Next. My aim is to retrieve data from local storage, but I keep hitting roadblocks. middleware.ts import { key, timeEncryptKey, to ...

Error encountered: Parsing error in Typescript eslint - The use of the keyword 'import' is restricted

My CDK application is written in typescript. Running npm run eslint locally shows no errors. However, when the same command is executed in a GitLab pipeline, I encounter the following error: 1:1 error Parsing error: The keyword 'import' is r ...

Select a single radio button containing values that can change dynamically

<input type="radio" on-click="checkDefaultLanguage" id="checkbox" > [[names(name)]] This custom radio input field contains dynamic values and I am attempting to create a functionality where only one radio button can be selected at a time while dese ...

ServiceAccountKey cannot be located by Firebase deploy

I am in the process of integrating a serviceAccountKey into my Typescript cloud functions project. var serviceAccount = require("/Users/kareldebedts/myAppName/functions/serviceAccountKeyName.json"); admin.initializeApp({ storageBucket: "appName.appspot ...

Ways to retrieve a variable from a separate TypeScript document

A scenario arises where a TypeScript script contains a variable called enlightenFilters$: import { Component, Input, OnInit } from "@angular/core"; import { ConfigType, LisaConfig } from "app/enrichment/models/lisa/configuration.model"; ...

What could be causing the type errors I am encountering while trying to resolve this Promise within a generic function?

I am attempting to implement additional types within this WebSocket protocol: type Action = { action: "change-or-create-state"; response: string; } | { action: "get-state"; response: string | null; }; /** * map an action to its response ...

Blend the power of Node's CommonJS with the versatility of Typescript's ES modules

I currently have a Node.js v10 legacy application that was built using CommonJS modules (require). The entire codebase is written in JavaScript. However, I am considering upgrading the app and refactoring a specific part of it to use TypeScript modules ( ...

Can you explain the variance between Next.js and Create React App?

I've been curious about understanding the distinctions between Next.js and Create React App (CRA). Both aim to simplify our lives when developing front-end applications with React. While researching online, I came across a key difference: Next.js o ...

Attempting to clear the value of a state property using the delete method is proving to be ineffective

Within my React-component, there exists an optional property. Depending on whether this property is set or not, a modal dialog is displayed. Therefore, when the modal should be closed/hidden, the property must not be set. My state (in simplified form): i ...

Guide to uploading files in Vue.js v3

I'm trying to implement file upload functionality using Vue.js version 3. Although I have successfully imported ref, I am unsure how to utilize it for retrieving file data? FileUploadTest.vue <template> <h1>File Upload</h1> <div ...

Is it possible for a service to retrieve a component's template?

I am faced with a scenario where two services (A and B) need to communicate with each other. Service A is required to build a chart based on asynchronous data received from service B, which is used in other areas so it operates independently. I attempted t ...