Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript type safety in switch case statements

I'm working with Redux and I'm trying to make my reducers type safe. I found some code example from the ngrx-store/example app where they perfectly succeed in doing this. (https://github.com/ngrx/example-app/blob/master/src/app/actions/book.ts)

While integrating this in my own project, I noticed something strange, which I cannot explain. Check the following code sample (some comments inline):

// Action has a type and payload property
interface Action {
    type: string;
    payload?: any;
}

// Here I declare the action types as plain strings
const FIRST = "FIRST";
const SECOND = "SECOND";

// I create classes for every action with there respective types
class FirstAction implements Action {
    public type = FIRST;
    payload: { id: number };

    public constructor(id: number) {
        this.payload = { id };
    }
}

class SecondAction implements Action {
    public type = SECOND;

    public constructor() { }
}

// Create a union type
type Actions = FirstAction | SecondAction;

// Use the union type as type parameter in my function
function test(action: Actions): void {
    switch (action.type) {
        case FIRST:
                    // compiler will complain it cannot find the payload 
                    // property on Actions
            let temp = action.payload.id;
        case SECOND:
        // empty
        default:
        //empty
    }
}

If I replace the definition of the FIRST and SECOND properties into the following, it does work.

export function type<T>(label: T | ''): T {
    return <T>label;
}


const FIRST = type("FIRST");
const SECOND = type("SECOND");

As far as I can see, the type function only casts the string back to a string. Why does the code work with calling the type function but not when declaring the strings immediately?

Here's a typescript playground example where you can just comment the definitons in or out (first with the working version).

like image 249
KwintenP Avatar asked Nov 19 '22 12:11

KwintenP


1 Answers

It's because the TSC compiler cannot distinct the 2 values:

const FIRST = "FIRST";
const SECOND = "SECOND";

It's both of type string, thus TSC doesn't know which belongs to what. You have to give it a type, and that's what you're doing by casting it with your type function.

But it's easier if you write it as follows:

const FIRST: "FIRST" = "FIRST";
const SECOND: "SECOND" = "SECOND";

Typescript playground

like image 188
Dieterg Avatar answered Dec 10 '22 01:12

Dieterg