Comments (6)
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.
@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
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:
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.
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 tounknown
- it's not a big deal for it. But your conditional type has to produce astring
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.
@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:
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 returntrue
since the constraint of this contextual type isnever
(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.
Feel free to send a (working 😉) PR
from typescript.
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)
- Source mappings are missing for serialized properties
- Missing overload method on union types HOT 7
- JSDoc `@import` of "tslib" fails under `--moduleResolution bundler`
- Interface that extends another no longer constrains types like the original HOT 1
- Proposal: Allow isolated declarations to infer results of constructor calls HOT 10
- Property 'difference' does not exist on type 'Set' HOT 3
- Type narrowing of condition leads to unexpected error for assignment HOT 3
- Increase the distinction between class and interface properties when defining a class or interface
- Add adaptation to class and interface property prompts HOT 1
- Weird behavior with recursive generic types HOT 2
- Array of unions changed to union of arrays in conditional type HOT 4
- tsserver requires `npm` to be installed on `neovim` trough `mason` HOT 2
- RangeError: Maximum call stack size exceeded when calling `getJsDocTags` on getter of class that implements itself
- Class constructors that early return another object still require fields to be assigned HOT 2
- [NewErrors] 5.7.0-dev.20240922 vs 5.6.2 HOT 6
- [ServerErrors][JavaScript] 5.7.0-dev.20240922 vs 5.6.2 HOT 3
- [ServerErrors][TypeScript] 5.7.0-dev.20240922 vs 5.6.2 HOT 7
- IsolatedDeclarations: emitted declarations inconsistent between `transpileDeclaration` API and TypeScript Playground HOT 1
- Inconsistent typechecking with require() in JS and TS HOT 5
- TypeScript fails to narrow union of native Error types HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typescript.