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

Callsite inference of a conditional string literal type fails when the literal type of the parameter would produce a valid expansion #59829

Closed
Oblarg opened this issue Sep 2, 2024 · 2 comments
Labels
Duplicate An existing issue was already created

Comments

@Oblarg
Copy link

Oblarg commented Sep 2, 2024

This is a bit of a weird one, bear with me...

As a hobby project/exercise, I've been working on (parts of) a literal regex engine using recursive TypeScript types. It is flatly impossible to do much of interest along these lines with first-class types, so instead I've been using conditional types to write validators that either resolve to never or to identity. This allows validation of literal function params, like a sort of SFINAE:

// this type can only be inferred to nonempty values
type NonEmpty<S extends string> = S extends "" ? never : S;
// we cannot call this with an empty string literal unless we intentionally lie to TSC about the type
function onlyAcceptsNonEmpty<S extends string>(param: NonEmpty<S>) ...

While mucking around with this, I found myself in a situation where TSC fails to infer such a parameter type in situations where the literal type of a provided string literal argument produces a valid expansion. Instead, it falls back to the broader string type, which fails to expand.

I've extracted a repro case here. Curiously, the behavior depends on the order of the recursive branches in the parameter type; if we swap the success check to the beginning of the type, the inference works as expected.

The relevant change between the two:

// this does not work for callsite inference
type ResolveQuantifiers<
  Regex extends string,
  Match extends string,
  S extends string = Match,
> = Regex extends `${NotQuantified<infer Prefix>}${Quantifier}${infer Suffix}`
  ? Regex extends `${Prefix}*${Suffix}`
    ? ResolveZeroOrMore<Prefix, Suffix, Match, S>
    : never
  : Match extends Regex
    ? S
    : never;

// this works for callsite inference
type ResolveQuantifiers<
  Regex extends string,
  Match extends string,
  S extends string = Match,
> =
  Match extends NotQuantified<Regex>
    ? S
    : Regex extends `${NotQuantified<infer Prefix>}${Quantifier}${infer Suffix}`
      ? Regex extends `${Prefix}*${Suffix}`
        ? ResolveZeroOrMore<Prefix, Suffix, Match, S>
        : never
      : never;

Note that in both cases, the ResolveQuantifiers type itself seems to behave as we would like, and if we provide a copy of the parameter as an explicit generic argument the compilation error goes away.

This seems like an unintended and undesirable behavior. Naively, I'd expect the inference engine to check the exact literal type of a literal parameter first, and infer the resulting generic if it evaluates to something other than never. What's going on here?

@Oblarg Oblarg added the Duplicate An existing issue was already created label Sep 2, 2024
@Oblarg
Copy link
Author

Oblarg commented Sep 2, 2024

No idea how the duplicate tag got on this; maybe from the "issue type" dropdown selector? Could someone either link the issue this duplicates or remove it?

@MartinJohns
Copy link
Contributor

No idea how the duplicate tag got on this

You used the wrong issue type. You used this one: https://github.com/microsoft/TypeScript/blob/main/.github/ISSUE_TEMPLATE/types-not-correct-in-with-callback.md?plain=1

This issue type is only meant to tell you: the described behavior is correct and intentional, as per #9998.

You should use the bug report issue type if you think you have a big report, or the "Other" one and acknowledge that issues using this template may be closed without further explanation at the maintainer's discretion.


Probably best to close this and file a new correct issue following the templates.

@Oblarg Oblarg closed this as completed Sep 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

2 participants