Giter Club home page Giter Club logo

Comments (6)

RyanCavanaugh avatar RyanCavanaugh commented on September 27, 2024 1

Naively, I'd expect the inference engine to check the exact literal type of a literal parameter first,

This is would happen if param were just param: S:

function test<S extends string>(self: S, param: ResolveQuantifiers<"xa*b*", NoInfer<S>>, ) {}
test("xabbbbb", "xabbbbb");

but instead TS has to try to "work backwards" from "xabbbbb" to ResolveQuantifiers<"xa*b*", S> to figure out what S should be, which is... not trivial, to say the least

Indeed if you set this up so that there's a straightforward inference site from param to S, this works exactly as desired (and would be much faster to typecheck!):

function test<S extends string>(param: S & ResolveQuantifiers<"xa*b*", NoInfer<S>>) {}
// OK
test("xabbbbb");
// Error
test("xabcbbbb");

from typescript.

ssalbdivad avatar ssalbdivad commented on September 27, 2024 1

@Oblarg For non-trivial parsing in TS, I'd highly recommend using a shift-reduce parser. Iterating over one character at a time is surprisingly performant in TS, often more so than matching against patterns in the middle of a string which can be much slower and also blow up into a union in many cases. So instead of relying on types like:

type Includes<Substring extends string> = `${string}${Substring}${string}`;

You'd using something like this to create abstractions of the parsing rules you need (this is an excerpt of the types ArkType uses for parsing):

export type shift<
  lookahead extends string,
  unscanned extends string,
> = `${lookahead}${unscanned}`;

type EscapeToken = "\\";

export type shiftUntil<
  unscanned extends string,
  terminator extends string,
  scanned extends string = "",
> =
  unscanned extends shift<infer lookahead, infer nextUnscanned>
    ? lookahead extends terminator
      ? scanned extends `${infer base}${EscapeToken}`
        ? shiftUntil<nextUnscanned, terminator, `${base}${lookahead}`>
        : [scanned, unscanned]
      : shiftUntil<nextUnscanned, terminator, `${scanned}${lookahead}`>
    : [scanned, ""];

export type includes<s extends string, token extends string> = shiftUntil<
  s,
  token
>[1] extends ""
  ? false
  : true;

type a = includes<"...:..", ":">;
//   ^? true
type b = includes<".....", ":">;
//   ^? false
type c = includes<":", ":">;
//   ^? true
type d = includes<"\\:", ":">;
//   ^? false

Playground

If you're interested, I've been looking for someone to add regex inference capabilities to ArkType which would involve very similar logic to what you're working on here:

arktypeio/arktype#695

Would love to have your help- it's one of the rare cases where you can do type-level parsing like this that also has real-world utility 🎉

from typescript.

Andarist avatar Andarist commented on September 27, 2024

From what I was able to gather this requires:

  • a fix for this issue. I have a local fix for it but I'm struggling to figure out how to handle properly recursive conditional types, like even the builtin Awaited. Since it is really just constrained to unknown - it's not a big deal for it. But your conditional type has to produce a string constraint
  • a an improved getRecursionIdentity for conditional types. Right now TS can't infer from your conditional type (with the fix for the point above) because this type is determined to be deeply nested. If I increase the depth limit... it succeeds.

If you could reduce your repro to a shorter one... that could help the investigation too ;p

from typescript.

Andarist avatar Andarist commented on September 27, 2024

@RyanCavanaugh I'm not sure if this fully warrants a Design Limitation label. Given that I was able to introduce changes to get it working is a sign to me that this is fixable:
Various examples of the discussed code

My changes are not fully correct so I can't open a PR (yet? :P) but they don't touch the inference algorithm at all. TS is perfectly capable of inferring this (even if it's not trivial!). The are 2 blockers here as I outlined above (see my comment):

  • it's a constraint issue, right now isLiteralOfContextualType doesn't return true since the constraint of this contextual type is never (that's because of #59868 )
  • and it's a getRecursionIdentity + invokeOnce(source, target as ConditionalType, inferToConditionalType); problem

After fixing~ the constraint* of the contextual type and allowing this conditional type to infer more than once it infers it OK.

*Well, I haven't really fixed, or at least not correctly. This is the biggest hurdle I have here - how to properly compute this constraint in a recursive scenario. Maybe this is he design limitation after all 😅

Note that the example above that errors still returns the literal string type (the type of the argument), even when the parameter's type gets computed to never. That's pretty normal though (TS playground):

declare function test<T extends string>(arg: T extends `${string}z` ? T : never): T

test('aaa')
// ^? function test<"aaa">(arg: never): "aaa"

That param: S & ResolveQuantifiers<"xa*b*", NoInfer<S>> is inspired. Nice one!

from typescript.

RyanCavanaugh avatar RyanCavanaugh commented on September 27, 2024

Feel free to send a (working 😉) PR

from typescript.

typescript-bot avatar typescript-bot commented on September 27, 2024

This issue has been marked as "Design Limitation" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

from typescript.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.