镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

intersection type between two template literal type will never be 'never' type #60446

Description

@anatawa12

🔎 Search Terms

intersection template type never

🕗 Version & Regression Information

  • I was unable to test this on prior versions than 4.5 because // ^? does not work on typescript playground

I think this is bug since template literal type is introduced in 4.1.

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.8.0-dev.20241106#code/GYVwdgxgLglg9mABAQzATwBQEoBcL2IDeiUAFgE5wDuiYIANvQNyIC+AUOxAgM5QkBTPgFY8AAwC2MZCDIB6ACSE+5GGADmrORFICIAazGIAZIjFKVazXNgSB9NQKMBefJixN2cuYl++AegD8Xj5+ZDA8iDykcAwAJogARgK0AgBuAuRJsohxcEK0cFDsQA

💻 Code

function any(): any { throw null; }

const test5: `miauth/${string}/check` & `${string}/timeline` = any();
//     ^?
//    this should be never but does not

🙁 Actual behavior

`miauth/${string}/check` & `${string}/timeline` will remain `miauth/${string}/check` & `${string}/timeline`

not coming never would cause problem with Indexed Access Types

function any(): any { throw null; }

type Endpoints = {
  'test/timeline': 0,
  'test2/timeline': 0,
  'other endpoint': 2,
  [ep: `miauth/${string}/check`]: 1, // no error at test2 if comment
}

type TimelineEndpoints = keyof Endpoints & `${string}/timeline`;

const test2: Endpoints[TimelineEndpoints] = any();
const test3: 0 = test2;
// This assignment should not have type errors,
// but because `miauth/${string}/check` & `${string}/timeline` is not never,
// test2 become 0|1 so we cannot assign test2 to test3.

playground

🙂 Expected behavior

`miauth/${string}/check` & `${string}/timeline` should become never and Endpoints[TimelineEndpoints] in snippet above should become 0

Additional information about the issue

Of course, we cannot assign variable typed `miauth/${string}/check` to `${string}/timeline` and vice versa.

function any(): any { throw null; }

const test0: `miauth/${string}/check` = any();
const test1: `${string}/timeline` = test0; // error here, as expected

playground

Real world case: misskey-dev/misskey#14885 (comment)

Activity

  1. Andarist commented on Nov 7, 2024

    @Andarist
    Contributor

    The problem is not exclusive to template literals - string mappings are also affected:

    const test6: Uppercase<string> & `a${string}` = {} as any;
    const test7: Uppercase<string> & Lowercase<string> = {} as any;
  2. jcalz commented on Nov 7, 2024

    @jcalz
    Contributor

    Relevant #40598 (comment)

    What about get${string} & ${string}Prop? Should that reduce to get${string}Prop? This could get complicated.

    I wanna say "no" - matching patterns up to patterns, while possible, I don't think is as intuitive as matching literals to patterns - I see leaving it as get${string} & ${string}Prop as fine. Especially since with something like a${string} & ${string}a, there's no simple template it could merge down to and actually be equivalent, without also extracting specific literal cases for common prefixes and postfixes, like "a" | a${string}a. So yeah, while it's possible, I think it's not as high value - there's probably more clarity in retaining the input patterns, anyway.

  3. RyanCavanaugh commented on Nov 7, 2024

    @RyanCavanaugh
    Member

    In general we don't have any static guarantees about any two types always producing never when intersected. There is definitely a gray line (literals do, some template literals do, but not all of them), but it is not computable in general to know if two arbitrary types have a nonzero overlap. I don't really want to complicate this logic any further than it already is just to further a problem that won't actually be solved.

  4. typescript-bot commented on Nov 10, 2024

    @typescript-bot
    Contributor

    This issue has been marked as "Not a Defect" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

  5. 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

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions