Skip to content

Literal String Union Autocomplete #29729

Not planned
Not planned
@MrTarantula

Description

@MrTarantula

Autocomplete works for literal string unions, but adding a union of string negates autocomplete entirely. This has been brought up before but I believe there is enough value in this feature to be reconsidered.

My use case is to have a union of string literals for several colors, but also allow hex codes without having to add 16.7 million string literals.

TypeScript Version: 3.4.0-dev.20190202

Search Terms: Literal string union autocomplete

Code

interface Options {
  borderColor: 'black' | 'red' | 'green' | 'yellow' | 'blue' | string
};

const opts: Options = {borderColor: 'red'};

Expected behavior:

image

Actual behavior:

image

Playground Link: https://stackblitz.com/edit/typescript-bwyyab

Related Issues: #12687 #13614

Activity

added
Design LimitationConstraints of the existing architecture prevent this from being fixed
on Feb 4, 2019
RyanCavanaugh

RyanCavanaugh commented on Feb 4, 2019

@RyanCavanaugh
Member

'black' | 'red' | 'green' | 'yellow' | 'blue' | string

From the compiler's point of view, this is just a very fancy way of writing string. By the time we're looking for suggestions at borderColor, all the literals are lost.

You could write something like this:
image

Naturally this doesn't stop you from writing "bluck". You might want to track #6579

sindresorhus

sindresorhus commented on Feb 4, 2019

@sindresorhus

By the time we're looking for suggestions at borderColor, all the literals are lost.

Why not improve the compiler to keep more metadata around?

MrTarantula

MrTarantula commented on Feb 4, 2019

@MrTarantula
Author

You could write something like this:

It may accomplish the same behavior, but that's not intuitive to me at all. I doubt I could have gotten there on my own, and I know I would have trouble explaining it to someone newly coming to TS from JS.

spcfran

spcfran commented on Mar 11, 2019

@spcfran

It would be great to have this built in, although understand it may be difficult to implement on the compiler.

In the meantime, a generic workaround based on @RyanCavanaugh's solution might help:

type LiteralUnion<T extends U, U = string> = T | (U & { zz_IGNORE_ME?: never })

type Color = LiteralUnion<'red' | 'black'>

var c: Color = 'red'                    // Has intellisense
var d: Color = 'any-string'             // Any string is OK
var d: Color = { zz_IGNORE_ME: '' }     // { zz_IGNORE_ME } placeholder is at the bottom of intellisense list and errors because of never 

type N = LiteralUnion<1 | 2, number> // Works with numbers too

See it in action here

BendingBender

BendingBender commented on Mar 12, 2019

@BendingBender

Would be great if this could be implemented. It has the potential to improve even core Node.js APIs. The hash.digest encondig param, for example should accept plain strings but there are values available on all platforms that could be enumerated for ease of use.

neurolag

neurolag commented on Jun 26, 2019

@neurolag
Contributor

Hey Guys
Though the issue still isn't solved but I found out that in TS 3.5.1 you don't even have to create some weird property in order to get the workaround done:

type LiteralUnion<T extends U, U = string> = T | (U & {});
let x: LiteralUnion<"hello" | "world">;
x = "hey";

While this code is perfectly valid, "hello", and "world" are still showing up in the autocompletion.
Thank you for this great fix, @RyanCavanaugh and @spcfran!

175 remaining items

iartemiev

iartemiev commented on Jan 8, 2025

@iartemiev

If I'm understanding correctly, the reason the suggested solutions work is that a union of a primitive type and some variation of an empty object produces the wrapper/boxed object type corresponding to that primitive.

For example, string & Record<never, never> appears to be equivalent to String, the return type of new String('').

This prevents the union from getting reduced to the wider type, as the compiler will not reduce a literal to an object type. However, assignability is preserved, so we're still able to assign an arbitrary string value in this case.

type Str1 = 'foo' | string;
//    ^? string

type Str2 = 'bar' | String;
//    ^? String | 'bar' 

let a: Str2;
a = 'foo'; // 'foo' suggested via autocomplete
a = 'bar'; // arbitrary string does not error

Is this the correct interpretation or am I making some incorrect assumptions along the way?

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

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @sindresorhus@frenic@jogibear9988@matthewrobertson@Lonli-Lokli

        Issue actions

          Literal String Union Autocomplete · Issue #29729 · microsoft/TypeScript