TypeScript union of string and string literals

This is currently considered a design limitation (see microsoft/TypeScript#29729) and/or a missing feature (see microsoft/TypeScript#33471) of TypeScript. From the type system’s point of view, string is a supertype of any string literal like "blue" or "red", and so a union string | "blue" | "red" is the same as string and the compiler aggressively reduces such unions to string. This is completely correct as far as type safety goes. But it’s not great from the point of view of documentation or IntelliSense, as you’ve seen.

Luckily the linked TypeScript issues suggest some workarounds which you might find useful. One is to represent the union type in a way that the compiler does not aggressively reduce. The type string & {} is conceptually the same as string, since the empty type {} matches any non-null and non-undefined type. But the compiler does not perform this reduction (at least as of TS 3.8). From this type you can build your union like (string & {}) | "red" | "green" | "blue", and the compiler will keep this representation long enough to give you IntelliSense hints:

function getColor(color: (keyof typeof knownColors) | (string & {})): string {
    if (color in knownColors)
        return knownColors[color as keyof typeof knownColors];
    return color;
}

This accepts and rejects the same inputs as before:

getColor("red"); // okay
getColor("mauve"); // okay
getColor(123); // error

But you can verify that IntelliSense produces the following:

IntelliSense suggesting "red","green","blue"


The type signature might be a little more confusing than you’d like. You could also get a similar effect by using overloads instead:

function getColorOvld(color: (keyof typeof knownColors)): string;
function getColorOvld(color: string): string;
function getColorOvld(color: string): string {
    if (color in knownColors)
        return knownColors[color as keyof typeof knownColors];
    return color;
}

getColorOvld("red"); // okay
getColorOvld("mauve"); // okay
getColorOvld(123); // error

This also gives you reasonable IntelliSense:

enter image description here


Okay, hope that helps!

Playground link to code

Leave a Comment