Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better type inference for choice()? #87

Open
KillyMXI opened this issue Apr 20, 2023 · 2 comments
Open

Better type inference for choice()? #87

KillyMXI opened this issue Apr 20, 2023 · 2 comments

Comments

@KillyMXI
Copy link
Member

KillyMXI commented Apr 20, 2023

Is it possible for choice(pa, pb, pc) to infer a parser of TA | TB | TC as a result?

export function choice<TToken,TOptions,TValue> (
  ...ps: Parser<TToken,TOptions,TValue>[]
): Parser<TToken,TOptions,TValue>;

Currently it requires a type hint - cast at least on of arguments to more common type.

First idea - change it like following:

export function choice<TToken,TOptions,TValue,TParsers extends Parser<TToken,TOptions,TValue>[]> (
  ...ps: TParsers
): Parser<TToken,TOptions,TValue>;

This doesn't work. I want to infer types from arguments, but now arguments are typed based on generic types.

Second idea:

export function choice<
  TToken,
  TOptions,
  TParser extends Parser<TToken,TOptions,TValue1>,
  TValue1 = TParser extends Parser<TToken,TOptions,infer T> ? T : never
> (
  ...ps: TParser[]
): Parser<TToken,TOptions,TValue1>;

This fails on a call without arguments (and I skipped testing it for more than 1 argument).
Maybe it would be possible to fix, but then I come to realization that it is not clear how this should be written for the Matcher overload.

My next thought:

I have an array of functions. I can get return type of a function with ReturnType<> utility if needed. Then I would need some contraption to turn an array into a unity type.
If there is a solution - it might even work for Matcher overload. And the case of an empty array would be a step in reduction.

Will get back to this.

@KillyMXI
Copy link
Member Author

With some help, I got it working:

export function choice<
  TToken,
  TOptions,
  TParsers extends Parser<TToken,TOptions,TValue>[],
  TValue = TParsers extends Parser<TToken,TOptions,infer T>[] ? T : never
> (
  ...ps: TParsers
): Parser<TToken,TOptions,TValue>;

(I was close)

And Matcher overload also seems to just work.

@KillyMXI
Copy link
Member Author

KillyMXI commented Apr 21, 2023

Hmm.

export function choice<
  TToken,
  TOptions,
  TParsers extends Parser<TToken,TOptions,unknown>[],
  TValue = TParsers[number] extends Parser<TToken,TOptions,infer T> ? T : never
> (
  ...ps: TParsers
): Parser<TToken,TOptions,TValue>;

Type tests are passing for any number of arguments.

But some previously working examples are now failing.

Overload 2 of 2, '(...ps: Parser<unknown, unknown, unknown>[]): Parser<unknown, unknown, unknown>', gave the following error.
  Argument of type 'Parser<Token, unknown, number>' is not assignable to parameter of type 'Parser<unknown, unknown, unknown>'.

Types are lost somewhere in certain practical scenarios.
This gets into esoteric category again (#67) - can't ship it like this.

@KillyMXI KillyMXI reopened this Apr 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant