-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Template literal type inference failure due to lazy placeholder matching #49839
Comments
This is pure speculation but I'm guessing what happens is:
It seems like in the first step we need to be applying the information about the constraint to the string-matcher-inference algorithm |
I love the |
@ahejlsberg maybe you would enjoy looking at this if you want |
I believe that the issue found by @jfet97 is essentially the same as the one reported here (or at least - it fails due to the same implementation details): type Exactly5<T extends string> = T extends `${any}${any}${any}${any}${any}${infer Last extends ""}` ? T : never
type test01 = Exactly5<"">
// ^? type test01 = never
type test11 = Exactly5<"a">
// ^? type test11 = never
type test21 = Exactly5<"ab">
// ^? type test21 = never
type test31 = Exactly5<"abc">
// ^? type test31 = never
type test41 = Exactly5<"abcd">
// ^? type test41 = never
type test51 = Exactly5<"abcde">
// ^? type test51 = "abcde"
type test61 = Exactly5<"abcdef"> // should be never
// ^? type test61 = "abcdef" It can be tested out in this TS playground. What I've seen in the compiler when debugging this looks very close to what @RyanCavanaugh has described here.
Could you elaborate on this one? I'm not sure if I understand this. I would assume that this should somehow be handled after the step 1. |
Right now the algorithm to infer to a template string doesn't "know" that it's trying to match against a type parameter with a constraint, so it doesn't realize that the inferences it's collecting are effectively possibly garbage. What we "should" do in this call declare function f<T extends "x" | "y">(a: `${string}.${T}`): T;
const z = f("ghi.jkl.x") is do something effectively the same (but more efficient) as inferring to |
The details of the template string implementation really confuse us in many scenarios, and maybe it needs time to improve |
Bug Report
🔎 Search Terms
template literal types, placeholders, pattern, inference, lazy/non-greedy
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
In the third call,
T
is inferred as"x" | "y"
🙂 Expected behavior
T
should be inferred as"x"
This came from this Stack Overflow question. It looks like the type
`${string}.${T}`
when compared to"ghi.jkl.x"
lazily matches`${string}`
as"ghi"
, so that it produces"jkl.x"
as an inference candidate forT
. This fails to match the"x" | "y"
constraint. So that's an invalid inference candidate.And I guess inference just fails, so
T
falls back to the constraint. Sure enough"ghi.jkl.x"
does match`${string}.x` | `${string}.y`
, so there's no error, but it's no longer useful as a generic call.Just wondering where this falls on the spectrum from "bug" to "intentional".
(Note that this situation isn't quite the same as two immediately adjacent placeholders as in #46124 or #47048 or #49411.)
(Also note that my workaround for this would be to write a recursive conditional type to actually find the last delimiter, as shown here, but it's yucky.)
The text was updated successfully, but these errors were encountered: