1. 27
  1. 3

    I think the title is misleading: This is not about simply using Redux and TypeScript but about improving on the recommended patterns by using advanced TypeScript features.

    1. 2

      That’s a good point. I can see in hindsight that it might sound like an introductory tutorial.

      Would it be good lobste.rs etiquette to edit the title after posting it?

      1. 3

        Since I updated the title on my website I updated the lobste.rs title to match. Since it’s only the sub-heading part I hope its obvious to people it’s the same story.

    2. 3

      Can someone give me good terms to Google to read about Typescript’s mapped types more academically? I find them super interesting (Parameters and ReturnType in particular seem interesting).

      1. 2

        Have you considered using an object with “as const” handlers instead of abusing class for this?

        const Handlers = {
          foo(state: State, { to }: { to: number }) { ... },
          bar(state: State, { to }: { to: number }) { ... },
        } as const
        • pro: more understandable to others that they can’t do class stuff like add properties or retain encapsulated state
        • con: required commas after functions
        1. 1

          Yeah, I would prefer not to use a class for clarity reasons too. Unfortunately, when I tried that it wouldn’t let me map over Handlers. Perhaps I’m missing something?

          error TS2749: 'Handlers' refers to a value, but is being used as a type here.
          1. 2

            In Typescript, classes are both a type representing an instance of the class, and a runtime value (the class itself in JS), as a kind of special case. Handlers here is just a runtime value (no magic) but its exact type is well-known at compile time. You can refer to its type (and the type of any runtime value) with typeof Handlers in type expressions.

            1. 1

              Thanks, I’ll give that a try and add an update to the blog post :)

              1. 2

                I think this is what you want?

                I’ve recently been trying to write my own version of this. I like your usage of an object as a declarative way of matching action names to handlers.

        2. 1

          Very cool. I’ve been exploring similar solutions lately but this one takes matters much further.

          One aspect I would change would be checking that every action is handled. Most Redux apps I see use multiple reducers via combineReducers, but every action is still run through every reducer in that case. So some actions definitely shouldn’t be handled. For that reason, I also want to use a single app-wide action type (to detect conflicting actions names).

          1. 1

            Thanks. The solution I end up with discards the handler for every action check since the Action types are derived from the handlers themselves. If you wanted multiple reducers I suppose you could create multiple Handler classes and derive an Action type for each, then union these for the overall Action type.

            Of course, if you have a separate handler function for each action anyway you won’t need any reducers.

          2. 1

            Nice writeup! The TS + Redux verbosity is something I’ve seen multiple projects and teams struggle with, and it’s becoming a bigger problem as reducer-like patterns become more popular outside of Redux. FWIW, the best prior art I’ve seen is typesafe-actions but it requires pretty heavy library buy-in. Nice to see some more DIY examples.

            Recently I played around a bit with ways of making tagged unions/ADTs less onerous to use and define in TS. I didn’t get super far but I might take some of this as inspiration, as Redux actions are a special case of ADTs.