Skip to content

Cannot infer Template Literal Type containing ${number} / ${string} #43060

Description

Bug Report

Cannot infer Template Literal Type containing ${number} / ${string}

🔎 Search Terms

  • Template Literal Types number infer
  • Template Literal Types string infer

🕗 Version & Regression Information

  • This is the behavior of version 4.1 or higher.

⏯ Playground Link

Playground link with relevant code

💻 Code

// ${number} issue
type A<T> = T extends `${infer U}.${infer V}` ? U | V : never
type B = A<`test.1024`>; // This will be "test" | "1024" as expected
type C = A<`test.${number}`>; // Expected "test" | `${number}` or "test" | number but this will be never
type D<T> = T extends `${infer U}.${number}` ? U : never
type E = D<`test.1024`>; // This will be "test" as expected
type F = D<`test.${number}`>; // Expected "test" but this will be never
// ${string} issue
type G<T> = T extends `${infer U}.${infer V}` ? U | V : never
type H = G<`test.hoge`>; // This will be "test" | "hoge" as expected
type I = G<`test.${string}`>; // Expected "test" | `${string}` or "test" | string but this will be never
type J<T> = T extends `${infer U}.${string}` ? U : never
type K = J<`test.hoge`>; // This will be "test" as expected
type L = J<`test.${string}`>; // Expected "test" but this will be never

🙁 Actual behavior

  • C type is never.
  • F type is never.
  • I type is never.
  • L type is never.

🙂 Expected behavior

  • Expected C type to be "test" | `${number}` or "test" | number.
  • Expected F type to be "test".
  • Expected I type to be "test" | `${string}` or "test" | string.
  • Expected L type to be "test".

Activity

  1. jcalz commented on Mar 22, 2021

    @jcalz
    Contributor

    I'm sure this goes without saying, but the issue here is not specific to ${number}, but occurs for all "pattern" template literals (as implemented in #40598) like ${string}.

  2. changed the title [-]Cannot infer Template Literal Type containing `${number}`[/-] [+]Cannot infer Template Literal Type containing `${number}` / `${string}`[/+] on Mar 22, 2021
  3. siefkenj commented on Mar 23, 2021

    @siefkenj

    I just came across this issue when trying to type a REST API. I made a type using templates that could successfully determine the appropriate return type of get("/sessions/43/posts"), however, my API is most commonly called as get(`/sessions/${sessionId}/posts` as const), which TypeScript failed to recognize.

    For my purposes, I would be happy if there were some way to tell TypeScript to interpret a template string as a string literal. E.g., `abc${x}` === "abc${x}".

    Some confusion I have with the current implementation is that type type of a const template seems to be inaccessible.

    When doing

    const x = 4.5;
    const y = `${x}abc` as const

    y will match against "4.5abc". However, in

    const x:number = 4.5;
    const y = `${x}abc` as const

    will not match against anything more specific than string (correct me if I'm wrong...), and yet TypeScript will show the type as `${number}abc`.

    Perhaps tagged-template syntax could be used to specify a template as abstract or weakly inferred?

    For example

    type A<T> = T extends `${infer U}.${infer V}` ? U | V : never
    type B<T> = T extends weak`${infer U}.${infer V}` ? U | V : never
    
    const x = 4.5
    const y:number = 4.5
    type X = A<`${typeof x}.abc`>  // "4" | "5.abc"
    type Y = A<`${typeof y}.abc`>  // never
    type WeakX = B<`${typeof x}.abc`>  // 4.5 | "abc"  OR  `${4.5}` | "abc"
    type WeakY = B<`${typeof y}.abc`>  // number | "abc"  OR  `${number}` | "abc"
    type WeakXMovedDecimal = B<`${typeof x}a.bc`>  // "4.5a" | "bc"  OR  `${4.5}a` | "bc"
    type WeakYMovedDecimal = B<`${typeof y}a.bc`>  // string | "bc"  OR  `${number}a` | "bc"
    
    const z1:`${typeof x}abc` = `${x}abc`  // Okay
    const z2:`${typeof y}abc` = `${y}abc`  // Fails typechecking
    const z3:weak`${typeof x}abc` = `${x}abc` // Okay
    const z4:weak`${typeof y}abc` = `${y}abc` // Okay

    I'm not sure if these rules would be consistent, but the basic idea would be to not expand const types to strings and allow weak template types to match substitutions directly.

  4. added
    FixedA PR has been merged for this issue
    SuggestionAn idea for TypeScript
    and removed
    Needs InvestigationThis issue needs a team member to investigate its status.
    on May 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

FixedA PR has been merged for this issueSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions