Skip to content

Template Literal Types reduces string union #54177

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

Closed
filipsobol opened this issue May 8, 2023 · 7 comments · Fixed by #54188 or #56434
Closed

Template Literal Types reduces string union #54177

filipsobol opened this issue May 8, 2023 · 7 comments · Fixed by #54188 or #56434
Assignees
Labels
Experience Enhancement Noncontroversial enhancements Fix Available A PR has been opened for this issue Suggestion An idea for TypeScript

Comments

@filipsobol
Copy link

Bug Report

When the template literal type is used in a union with strings that match its pattern, the union is reduced:

type union = 'downcast' | 'dataDowncast' | 'editingDowncast' | `${ string }Downcast`;
//   ^? type union = `${string}Downcast` | "downcast"

This is a problem when I want to use it as a parameter type, because then only "downcast" is suggested when calling the function:

function test( groupName: union ): void {}

test('') // Only "downcast" is suggested

As a workaround, I can use function overloading, where one signature has union with all string literals and another only the template literal type, but it would be nice to get this issue fixed:

function test2( groupName: 'downcast' | 'dataDowncast' | 'editingDowncast' ): void;
function test2( groupName: `${ string }Downcast` ): void;
function test2( groupName: string ): void {}

test2('') // All "downcast", "dataDowncast" and "editingDowncast" are suggested

🕗 Version & Regression Information

From 4.1 (when template literal types were introduced) to 5.1.0-beta, tested in TypeScript Playground.

⏯ Playground Link

Link to the TypeScript Playground

🙁 Actual behavior

Only string literals that don't match the template literal type are displayed.

🙂 Expected behavior

All string literals should be shown.

@MartinJohns
Copy link
Contributor

MartinJohns commented May 8, 2023

This sounds very much like #33471.

@Andarist
Copy link
Contributor

Andarist commented May 8, 2023

As a workaround you can do this:

type union = 'downcast' | 'dataDowncast' | 'editingDowncast' | `${ string & {} }Downcast`;

@filipsobol
Copy link
Author

It doesn't work. For example, passing the string 'testDowncast' reports an error because it doesn't match any value from the union.

function test( groupName: 'downcast' | 'dataDowncast' | 'editingDowncast' | `${ string & {} }Downcast` ): void {}

test('testDowncast') // Throws an error

function test2( groupName: 'downcast' | 'dataDowncast' | 'editingDowncast' | `${ string }Downcast` ): void {}

test2('testDowncast') // Works

@Andarist
Copy link
Contributor

Andarist commented May 8, 2023

Oh, I see. I have a fix for this locally - might wrap it up in a PR later. I think that this should work since this string & {} is already handled by the compiler in different places and it's meaning is somewhat established.

@fatcerberus
Copy link

It definitely should work - all strings are also string & {} by definition, as {} is a supertype of string. (of course by the same token you could argue that the types are equivalent and shouldn’t behave differently but that’s a different can of worms)

@Andarist
Copy link
Contributor

Andarist commented May 8, 2023

Opened a PR to help with the proposed workaround: #54188

@ahejlsberg
Copy link
Member

I'm reopening this issue per our discussion in the last design meeting. We already support the pattern

type Foo = "choice1" | "choice2" | string & {};

to produce an un-reduced union for purposes of statement completion. We want to support a similar pattern for template literal types by intersecting with {} at the top level (rather than inside a placeholder as implemented by #54188). For example:

type Foo = "choice1" | "choice2" | `choice${string}` & {};

This means that the type in the original post should be written

type union = 'downcast' | 'dataDowncast' | 'editingDowncast' | `${ string }Downcast` & {};

I will put up a PR to that effect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Experience Enhancement Noncontroversial enhancements Fix Available A PR has been opened for this issue Suggestion An idea for TypeScript
Projects
None yet
7 participants