Have TypeScript Infer the Callback Function Type
Like before, let's start by diving into
CMD + Click:
//inside node_modules/@types/react/index.d.tsfunction useCallback<T extends Function>(callback: T, deps: DependencyList): T;
We can see that
useCallback accepts a function `
0:01 To figure out the solution here, we're going to dive into useCallback itself. Let's come onClick and see what's going on inside useCallback. We can see that T inside here, this is the thing that it's inferring. This is the thing that gets passed into the type argument.
0:15 We're saying this has to be a function and this function is something that's global in TypeScript. This isn't something that comes from React. It's saying, any function can be passed in here. Nice.
0:29 That T then, well, they could call it T function, but they call it T, gets passed into the callback. We've got a DependencyList here and then it returns the function that you pass in. The thing that's in the type argument here needs to be a function.
0:44 The thing that we're passing in here, "Type 'string' does not satisfy the constraint 'Function'." We're trying to say inside here that we're passing it a string, so the buttonName needs to be a string. This is a fairly common issue that I see when people don't understand how these callbacks behave, and certainly how the type arguments reinferred.
1:03 If I remove this, then what does useCallback suddenly get here? We hover over this. We can see that useCallback now, the type argument is being inferred as (buttonName: any) => void. We're super duper close already, because we're expecting this to be (buttonName: string) => void.
1:22 If we type this as a string, what happens? It works. Beautiful. Now, useCallback is being passed in as (buttonName: string) and returns void. We return something from here, we return a number, let's say. Now, onClick will be slightly different. It's now a (buttonName: string) that returns 124123. That's pretty cool.
1:44 What we're seeing here is that useCallback takes in the function that's passed in, infers its types, and then uses that as the thing that gets returned. That's really nice to think about.
1:55 We could do this in an alternate way too. We could say that buttonName inside here. We pass this in as a (buttonName: string) and then return void. Now, it's working, even though we're internally returning a different return type.
2:11 That's because technically you can pass a number to void. It doesn't really matter what it does, because if you pass void, the consuming function of the thing calling your function isn't expecting to do anything with it.
2:24 That's an alternate way that you can do this, but it's much cleaner if you rely on the inference and you pass (buttonName: string) inside here. This should give you a good idea of what useCallback demands from you when you use it from a TypeScript standpoint.