Skip to content

Intersection type in template literal is not reduced to its bare typeΒ #57918

@unional

Description

@unional

πŸ”Ž Search Terms

template literal, intersection type

πŸ•— Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play?#code/C4TwDgpgBAysBOBLAdgcwCrggHgNJQgA9gJkATAZygoRVQD4oBeKfIk8qmpNKAMigoAZhHhQAqgCgAkAH4oAbVwAaCQF0CxUpUXjVuNTPkBvAL6aOO44oAKg5FAAGAEmO5TjtQC4oAV2QA1sgA9gDuDqZGUADkJAC2YAA2AIYkADKIJPDJidEyPtHcdBlZOXnSBUVo5T7IEABuopKSoJBQAEoAjMywtGiYkNhVDJIA9KNQkwB6si1YHQBMPXA8GFhDfaj8UNbJPt2m9GMT07Nzbe0AzMubAzjRyQBGAMbRR+OTUDPn0O0ALDdVndsA8XtFtrt9lBDsdPt8fh0AKyAujAlzGZC+OKPUQed4nL6zVq-ABsKP663RmOxuMcEKgeygB3xcLOUCAA

πŸ’» Code

type StringType<K extends string> = K extends string & infer U
	? [K, U] extends [U, K]
	? {} extends { [P in `${K}`]: unknown }
	? 'templateLiteral'
	: 'stringLiteral'
	: 'string'
	: never

type R1 = StringType<string>
//   ^? type R1 = "string"
type R2 = StringType<string & { a: 1 }>
//   ^? type R2 = "string"

type R3 = StringType<'abc'>
//   ^? type R3 = "stringLiteral"
type R4 = StringType<'abc' & { a: 1 }>
//   ^? type R4 = "templateLiteral" <-- should be "stringLiteral"

type R5 = StringType<`${number}`>
//   ^? type R5 = "templateLiteral"
type R6 = StringType<`${number}` & { a: 1 }>
//   ^? type R6 = "templateLiteral"

πŸ™ Actual behavior

type R = `${'abc' & { a: 1 }}`
// did not reduce => `${'abc' & { a: 1 }}`

πŸ™‚ Expected behavior

type R = `${'abc' & { a: 1 }}`
// should reduce to => `${'abc'}`
// => `abc`

Additional information about the issue

I mentioned this in #54648 after it is closed. It is limiting our ability to write the types that works with string literal and template literal and there is no alternative way to workaround that.
I'm suggesting this issue should be fixed and restore the behavior in 5.0.

Here is my original comment:

This behavior is causing a few types in type-plus to fail (e.g. IsTemplateLiteral, IsStringLiteral, Omit, IsNegative, etc) unional/type-plus#429.

In term of soundness, IMO it does make sense that ${string & { a: 1 }} to be reduced to ${string}.

in JS, it would be:

const extendedStr = Object.assign('abc', { a: 1 })
console.log(`${extendedStr}`) // 'abc'

the reasoning being the toString(): string remains unchanged thus the resulting type should be safe to reduce.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions