Two Approaches to Working with Class Names
The first solution is to add
TVariant which extends
string. From there, we'll take the
Record from there where
TVariant will be the key along with the
const createClassNamesFactory =<TVariant extends string>(classes: Record<TVariant, string>)
0:00 We have two solutions here. The first solution is to take TVariant, which is going to be a string, and then do this thing where we say, "OK, we want the record of that." That TVariant is going to be the key, and then we're going to extract the strings from there.
0:18 That's the only generic we need, actually, because we don't really care about the thing that gets returned or infer in the literal of it. All we need to do is say createClassNamesFactory, we end up with primary or secondary in the type argument, and we get getBg, which is then primary or secondary.
0:36 That means we get this lovely bit of auto-complete here, which is what we wanted anyway. Then the otherClasses is represented as an array of strings, which gets appended onto the end. This is one way of constructing it.
0:50 The other way of doing it is by representing TClasses as a Record of string, string.
0:55 Here, if we got this wrong, for instance, and it was string and number, let's say, then all of these would yell at us for not being assignable to type number, but then TClasses gets to represent just that thing. That whole box gets put into the type argument, which is nice too.
1:15 Again, instead of representing TVariant here, then what we get is keyof TClasses. This is a very similar exercise to what we've done before, and you notice that we don't have to work too hard to get all of this beautiful generic inference working.
1:33 Again, it's giving us complaints here because we can't pass in something that isn't an object, so it's all working beautifully. Well done.