1

I have a function like this in Typescript:

function combine<A, B>(p1: Parser<A>, p2: Parser<B>): Parser<A | B> { … }

Is it possible to type it for variable number of type variables (A, B, C, …) and correctly set the result type to Parser<A | B | C | …>? I know I can do this by writing the type signatures by hand for different arities:

function combine<A>(p1: Parser<A>): Parser<A>;
function combine<A, B>(p1: Parser<A>, p2: Parser<B>): Parser<A | B>;
// …and so on…
function combine(...parsers: Parser<any>[]): Parser<any> { … }

Is that the only option?

PS. I was looking at this similar question, but the types are a bit above my head and I don’t know whether it’s the same case or not (the “endless union type” looks like an extra requirement here).

0

2 Answers 2

2

Here's how you you can infer the result type based on the arguments. Probably that's what you're trying to achieve

function combine<T extends string>(...ps: Parser<T>[]): typeof ps extends Array<infer R> ? R : never {
    throw new Error('not implemented')
}

TS Playground


Edit:

After reading your comment I realized that you probably want to define the function like this:

// generic extending parser Array
function combine<T extends Parser[]>(...ps: T): typeof ps extends Array<infer R> ? R : never {
    throw new Error('not implemented')
}

this way you'd be able to pass a tuple as generic argument to define the type and the position of the arguments

const result = combine3<[B, A]>(parserB, parserA)
// typeof result is B | A

or don't enforce position, just let ts infer the return type

const result = combine(parserB, parserA, parserA, parserC)
// typeof result is B | A | C

examples

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you, but I need the A, B, C types to be unrelated. In other words, I need a function signature similar to foo<A, B, C, …>(arg1: A, arg2: B, arg3: C, …): A | B | C | ….
@zoul I think I've now understood the question, so I've updated my answer. Hope that helped
1

It is possible to implement it with array of generic types instead of unions. You can do in few ways:

  • Create a type and has abbility to do many arrow function implementation

    type ParserFunc<T extends any[]> = (...args: {
      [P in keyof T]: T[Parser<P>]
    }) => Parser<T[number]>
    
    const function1: ParserFunc<[string,number]> = (p1, p2) => { ... }  // => Parser<string | number>
    const function2: ParserFunc<[string,string]> = (p1, p2) => { ... }  // => Parser<string>
    const function3: ParserFunc<[string,boolean]> = (p1, p2) => { ... } // => Parser<string | boolean>
    
  • Create a single standart function

    function combineParser<T extends any[]>(...args: {
      [P in keyof T]: T[Parser<P>]
    }): Parser<T[number]> {
      // ...
    }
    
    combineParser(1,'2',true, null) // => Parser<number | string | boolean | null>
    

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.