Hey you! Do you want to get the return type of a function and use it, without knowing what it really is, or asking the user of your thing to supply it outright?

Of course you do.



type GetReturnType < original extends Function > = original extends (... x : any []) => infer returnType ? returnType : never

Conditional types in typescript allow you to introduce type variables into the expression in a rather dynamic way. Notice the infer keyword. That says to TypeScript: "I want to take whatever TypeScript infers to be at this position and assign it to the name returnType ". It just so happens that the thing at that position is the return type of a given function, that we have called original .

What happens when you put it into action? I AM SO GLAD YOU ASKED! Take this extremely powerful, useful, and all-round fantasmical Higher Order Function



const someRandomStuff = < fn extends Function > ( originalFn : fn ) => { const result : GetReturnType < fn > = originalFn ( 12345 ); return result ; }

The type of result is going to be whatever the return type of originalFn(12345) is. We don't actually know, until...



const innerFn = ( item : number ) => item . toString ();

FYI, TS will infer the output of innerFn to be a string. Then...



const output = someRandomStuff ( innerFn ); // ALL SYSTEMS GO

Would you like to play a game? What is the type of output ? The answer is: What is the type of innerFn ?

It's a string!

If it weren't for this trick, the type of output would be any . I know, I was surprised too. But it's true.

You can do the same sort of thing with anything that TypeScript can infer. Wanna get the type of arguments?



type NYET = never // preparation for unfunny joke type GetArgumentType < original extends Function > = original extends (... x : infer argumentsType ) => any ? argumentsType : NYET

That's all, see you next time.