Advanced Generics 9 exercises

Create a Factory Function to Apply Type Arguments to All Child Functions

The first thing we'll do is create a new makeUseStyled function, and move the existing useStyled function into it to be returned:

const makeUseStyled = () => {
const useStyled = <TTheme = {}>(func: (theme: TTheme) => CSSProperties) => {
return {} as CSSProperties

Loading solution


0:00 Let's solve this together. We want a function called const makeUseStyled, and what that's going to do is it's just going to return useStyled.

0:11 Now, useStyled down here is going to break, so let's say const useStyled equals makeUseStyled. Now, what we need to do here, useStyled still currently accepts a type argument, so we need to remove the type argument from useStyled and put it on the top level. This is fairly simple.

0:31 We just take this and we just stick it up there, so TTheme. Now what we're getting is we're expected zero type arguments but got one. I can actually remove both of these and I'll put it just here. Whoops, copied the wrong section. Now, TTheme doesn't make sense, so we need to pass in MyTheme into makeUseStyled.

0:53 Now, just with that little refactor moving this from there to here, we can now get a fully typed version of useStyled that we can just consume. You can imagine now that useStyled and MyTheme can sit in one module together, and they can export useStyled, and then buttonStyle and divStyle can just consume from useStyled here without needing to pass in a type argument.

1:19 This solution is really, really useful, and I wish more libraries did it. It lets you pass in type arguments at the top level, that are then inferred everywhere, almost like a global, except it's not global because it's just scoped to this one function here. Extremely useful pattern, and what it does is it lets you have a reusable type argument at the top level which just filters all the way down.

1:47 Unfortunately, the only way to do this is by wrapping it in a factory function, because we know that in TypeScript, generics are tied to function calls. Making another function call at the top level which doesn't do anything, it's just literally an identity function where you pass in a type argument, just by doing that little step there, you get really beautiful inference in the rest of your app.