Avoid Duplicate Code in an Identity Function with Generics
The Wrong Solution
Let's start by looking at a solution that isn't optimal.
Here we have an interface ConfigObj
where we take in TRoute
:
interface ConfigObj<TRoute extends string> { routes: TRoute[]; fetchers: { [K in TRoute]?: () => any; };}
The routes
expr
Transcript
0:00 Let's look at a solution that doesn't work. We have an interface ConfigObj here, where we're taking in TRoute. This routes expresses TRoute. fetchers expresses K in TRoute. This is like a map type which takes this and then returns a function that returns any.
0:18 This works now. It means that this does not exist. It's going to yell at us because we don't have this thing here. It's not expressed in the routes. We're going to get autocomplete based on each of these three properties. If we pass contact in there, then we're going to get that.
0:36 But we're having to specify it like this. In other words, we're having to add a type annotation to it in order for it to work. We're actually having to duplicate quite a lot of code between the routes and between here. Ideally, this fetchers object would infer from the routes. The issue here is that you need a function call to map these two things together. That's why identity functions exist.
1:02 Let's take a look at the real solution. The real solution is to have TRoute extends string. Then this makeConfigObj, config ConfigObj TRoute, returns config. It's just an identity function. Now we're using exactly the same definition as we had in the previous exercise or the previous solution. This one is just the same as this one.
1:25 Now it just works. You don't need to add any extra annotations here. That's because this array, about, contact, is being inferred from these routes. If I add a new one, if I add wow like this, then wow is going to be added there. It's going to be inferred in that position. Extremely cool.
1:47 If I just remove this, zoom in a little bit, it means that you can use these types of functions to just get these nice little bits of configuration all sorted out for you. There's one more thing too which is so cool, which is identity functions are actually removed by most minifiers.
2:07 If you're worried that you're having to declare a bit of extra code for this...Surely, I should just be able to put the ConfigObj in. That'll be -- I don't know -- faster or fewer bundle size, let's say. Your minifier will be able to figure out that this is just an identity function and actually remove the function and the function call at runtime.
2:28 Even though this looks like an extra wrapper that you're adding to your application and your runtime, it actually doesn't work that way. It's actually going to be stripped out when the code is run. Super-cool.