You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Template literal wrong inferred value when multiple placeholdersΒ #47048
After debugging a bit inferFromLiteralPartsToTemplateLiteral in checker.ts, it seems p = getSourceText(s).indexOf(delim, p) searches for 2 in a targetTexts being ['', 2, ''] yielding a wrong result.
It's not clear to me what the actual algorithm should be here as I haven't found any spec or fundamental rules behind inferring template placeholders like above.
This is working as intended, though I'll grant it's a bit odd.
The key behavior to understand is that for two immediately adjacent template literal type placeholders we always match a single character from the source to the first placeholder. For example, when matching 'abcd' against `${string}${infer T}`, we'll match the first character against ${string} and thus infer 'bcd' for T. This holds even when the placeholder is ${number}, so matching '1234' against `${number}${infer T}` will match '1' against ${number} and infer '234' for T.
When resolving the d9 conditional type, we first perform inference for T. So, we match '2234' against `${number}${infer T1}${number}`, which matches the first '2' against the first ${number}, infers the second '2' for T, and matches '34' against the last ${number}. Following inference, we instantiate with the type inferred for T, leaving us with the conditional check '2234' extends `${number}2${number}`. Now, because the placeholders have intervening text, we scan the source string for the first occurrence of the separator '2'. That means we end up with an matching an empty string to the first ${number} and the string '234' to the second ${number}. This fails, and therefore the conditional type resolves to never.
Our matching is not particularly sophisticated, but that is in part by design because we don't want the compiler to be a full regex engine. In general, immediately adjacent placeholders are really only useful for taking strings apart one character at a time.
Activity
ahejlsberg commentedon Jan 11, 2022
This is working as intended, though I'll grant it's a bit odd.
The key behavior to understand is that for two immediately adjacent template literal type placeholders we always match a single character from the source to the first placeholder. For example, when matching
'abcd'against`${string}${infer T}`, we'll match the first character against${string}and thus infer'bcd'forT. This holds even when the placeholder is${number}, so matching'1234'against`${number}${infer T}`will match'1'against${number}and infer'234'forT.When resolving the
d9conditional type, we first perform inference forT. So, we match'2234'against`${number}${infer T1}${number}`, which matches the first'2'against the first${number}, infers the second'2'forT, and matches'34'against the last${number}. Following inference, we instantiate with the type inferred forT, leaving us with the conditional check'2234' extends `${number}2${number}`. Now, because the placeholders have intervening text, we scan the source string for the first occurrence of the separator'2'. That means we end up with an matching an empty string to the first${number}and the string'234'to the second${number}. This fails, and therefore the conditional type resolves tonever.Our matching is not particularly sophisticated, but that is in part by design because we don't want the compiler to be a full regex engine. In general, immediately adjacent placeholders are really only useful for taking strings apart one character at a time.
typescript-bot commentedon Jan 15, 2022
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.