jamesbirtles/fpreact

Change msg to allow for stronger types

Closed this issue · 1 comments

At the moment it is not really possible to strongly type a msg without using type assertions. I had initially tried to get around this by adding the generic .get function but this is ugly and is really just a type assertion in disguise. I realise now that redux already has this sorted, so we can pretty much just adopt that.

I'll demonstrate this with the counter example

enum Msg {
    Increment,
    Decrement,
    SetCounter,
    TrackInterval,
    // ...
}

interface IncrementMessage {
    kind: Msg.Increment;
}
interface DecrementMessage {
    kind: Msg.Decrement;
}
interface SetCounterMessage {
    kind: Msg.SetCounter;
    value: number;
}
interface TrackIntervalMessage {
    kind: Msg.TrackInterval;
    value: number;
}

type CounterMessages =
    | IncrementMessage
    | DecrementMessage
    | SetCounterMessage
    | TrackIntervalMessage;

const Counter = component<Model, CounterMessages>({
    update(msg, model) {
        switch (msg.kind) {
            case Msg.Increment:
                return { ...model, counter: model.counter + 1 };
            case Msg.Decrement:
                return { ...model, counter: model.counter - 1 };
            case Msg.SetCounter:
                return { ...model, counter: msg.value };
            case Msg.TrackInterval:
                return { ...model, interval: msg.value };
        }

        return model;
    },
});

Could probably even simplify this to

// Defined in fpreact somewhere
interface Message<K, V = undefined> {
    kind: K;
    value: V;
}

// In your component
type CounterMessages =
    | Message<Msg.Increment>
    | Message<Msg.Decrement>
    | Message<Msg.SetCounter, number>
    | Message<Msg.TrackInterval, number>;