Exhaustive typecheck where an unknown string is checked against all enum possibilities
00:18 13 May 2025

I want to do a kind of opposite exhaustive check as the one mentioned here:

How do I check that a switch block is exhaustive in TypeScript?

ie. something like this:

enum Color {
    Red = "Red",
    Green = "Green",
    Blue = "Blue"
}

function convertStringToColor(str: string ): Color {
    switch(str) {
        case Color.Red:
        case Color.Green:
            return str;
    
        default: {
            assertNotStillColor(str); // ??? 
            throw new Error("String did not match any of the known enum values"); 
        }
    }
}

That is, if we've forgotten to check all of our enum types, we should see a type error. But the default code block can still be enountered, without a type error, in the scenario that a non-matching string is encountered.

The typical exhaustiveness check does not work in this scenario - the type of str is still string.

I've tried something like this:

function assertNotStillColor(str: Exclude){

}

This theoretically could work, but TypeScript as I understand it, doesn't have a concept of 'any string except these ones'.

This does work:


type KnownStrings = "Foo" | Color; 
function assertNotStillColor(str: Exclude){

}

function convertStringToColor(str: KnownStrings ): Color {
    switch(str) {
        case Color.Red:
        case Color.Green:
        case Color.Blue: 
            return str;
    
        default: {
            assertNotStillColor(str);
            throw new Error("String did not match any of the known enum values"); 
        }
    }
}

But this isn't really what we want - we want this function to accept any string.

I suppose I can do this:

function convertStringToColor(str: string ): Color {
    const str2 = str as Color; 
    switch(str2) {
        case Color.Red:
        case Color.Blue: 
        case Color.Green:
            return str2;

    
        default: {
            assertNotStillColor(str2); 
            throw new Error("String did not match any of the known enum values"); 
        }
    }
}

It works.

Anything better?

typescript