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

Template literal wrong inferred value when multiple placeholders #47048

Closed
marcj opened this issue Dec 6, 2021 · 2 comments
Closed

Template literal wrong inferred value when multiple placeholders #47048

marcj opened this issue Dec 6, 2021 · 2 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@marcj
Copy link

marcj commented Dec 6, 2021

Bug Report

πŸ”Ž Search Terms

template literal wrong infer

πŸ•— Version & Regression Information

Tested in version 4.1.5, 5.2.3, 4.3.5, 4.4.0, 4.5.2, 4.6.0 nightly, everywhere the same behaviour.

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type d8 = `1234` extends `${number}${infer T1}${number}` ? T1 : never; //'2'
type d9 = `2234` extends `${number}${infer T1}${number}` ? T1 : never; //never

πŸ™ Actual behavior

d9 is computed as never.

πŸ™‚ Expected behavior

d9 should be computed as '2'.

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.

@DanielRosenwasser DanielRosenwasser added the Bug A bug in TypeScript label Dec 7, 2021
@DanielRosenwasser DanielRosenwasser added this to the TypeScript 4.6.0 milestone Dec 7, 2021
@ahejlsberg
Copy link
Member

ahejlsberg commented 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' 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.

@ahejlsberg ahejlsberg added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Bug A bug in TypeScript labels Jan 11, 2022
@ahejlsberg ahejlsberg removed this from the TypeScript 4.6.0 milestone Jan 11, 2022
@ahejlsberg ahejlsberg removed their assignment Jan 13, 2022
@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants