The `keyof typeof` Pattern
The solution is to turn
classNamesMap into a type and derive its keys from there.
Before we do this, let's look at an example of the process in action.
typeOf to Derive Types from Variables
type Example = typeof classNamesMap;
typeof acts like a fu
00:17 Well it's actually doing something different from what you're used to. On the type level, typeOf actually acts like a function. It takes in the thing on the right hand side and returns a type of what that thing has been inferred to. So if you hover over ClassNamesMap, you can see that it has primary, secondary, and success here.
00:36 And if you hover over example, it's exactly the same readout as when we hovered over this. So this basically just takes what TypeScript understands that variable to be and turns it into a type. So now, how do we take the key of that? How do we get primary, secondary, and success? How do we get this from this?
00:55 Well, what we do is we just say keyOf. So keyOf typeOf. It's not the other way around. It's not typeOf keyOf. Because typeOf keyOf makes no sense because the thing to the right hand side of typeOf needs to be a runtime variable. So we're basically saying keyOf typeOf ClassNamesMap.
01:13 We could even just extract this into its own type here, ClassNamesMap type. And then type keys equals keyOf ClassNamesMap type. Can't type. There we go. Now these keys then are primary or secondary or success.
01:32 And if we add a new one here, like something, let's just give it an empty one, this is going to be added here too. So something is there now there. Beautiful stuff. So how do we then use it? Well, we can just stick this down here. So we can say variant is keys here. And let's actually rename this something more sensible to variant there.
01:50 And we don't need to do this kind of like all of this song and dance, like declaring all of these separate types. We can literally just delete all that and just say keyOf typeOf ClassNamesMap. And that is the solution. And it's really, really pretty. And it means that you can just have these data structures that just infer all the way down.
02:10 You have actually just your ClassNamesMap, which is your source of truth, the thing that's happening at runtime. And then the props, the actual behavior can be inferred from the runtime behavior. It makes everything really, really nice and solid. And it means that you're not needing to update types all over the place.
02:27 This pattern is so, so useful and we're really just getting started with it.