Search code examples
typescriptclassswitch-statementdenotypeguards

Type guarding class in switch statement


I haven't use Typescript in many years and can't remember nor find how to type guard many classes inside a switch statement correctly.

class A {}
class B {}
class C {}

type OneOfThem = A | B | C;

function test(foo: OneOfThem): string {
    switch(/* something using foo */) {
        /* A */:
            return "A";
        /* B */:
            return "B";
        /* C */:
            return "C";

        /* should not need to use "default" as all cases are handled */
    }
}

I found and tried several options like:

but none of them works (Function lacks ending return statement and return type does not include 'undefined').

Is my memory playing tricks and this is not possible with classes ?


Solution

  • adding an extra member to the three classes to be used in the switch statement

    That is indeed probably the simplest solution for your case: it uses Discriminated Union, by adding a discriminant member to each class, with literal types (i.e. not just string or number), so that you (and TypeScript) can tell apart foo is from which class:

    class A {
        readonly kind = "a"
        //       ^? "a" string literal, not just `string`
    }
    class B {
        kind: "b" = "b"
        // ^? "b" string literal, as explicitly typed
    }
    class C {
        kind = "c" as const
        // ^? "c" string literal, thanks to the const assertion
    }
    

    Then simply differentiate based on this kind discriminant property:

    function test(foo: OneOfThem): string { // Okay
        switch (foo.kind) {
            case "a":
                foo
                //^? A inferred by TS, based on `foo.kind === "a"` type guard
                return "A";
            case "b":
                foo
                //^? B
                return "B";
            case "c":
                foo
                //^? C
                return "C";
            /* should not need to use "default" as all cases are handled */
        }
    }
    

    Playground Link