Reputation: 104115
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).
Upvotes: 1
Views: 2181
Reputation: 32148
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')
}
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
Upvotes: 1
Reputation: 523
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>
Upvotes: 0