Skip to content

Template literal wrong inferred value when multiple placeholdersΒ #47048

Closed

Description

@marcj

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.

Activity

  1. ahejlsberg commented on Jan 11, 2022

    @ahejlsberg
    Member

    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.

  2. added
    Working as IntendedThe behavior described is the intended behavior; this is not a bug
    and removed
    BugA bug in TypeScript
    on Jan 11, 2022
  3. typescript-bot commented on Jan 15, 2022

    @typescript-bot
    Contributor

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

  4. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Working as IntendedThe behavior described is the intended behavior; this is not a bug

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions