type Parent = {
fun<T>(a: T, b: string):void
}
type ChildFunArgs = Parameters<Parent['fun']<number>> // got an error
If the above code can run, so I can do this:
const childFunArgs: ChildFunArgs = [1, '1']
There's no higher-order type manipulation that will do this for you automatically purely in the type system.
If you are okay with something with some small runtime impact (meaning it emits some JS code) and with ugly nasty looking type munging, and if you are using TypeScript 3.4 and above, you can get your type like this:
const dummyParams =
((x => () => x) as <A extends any[]>(
f: (...args: A) => any) => () => A
)(null! as Parent['fun'])<number>();
type ChildFunArgs = typeof dummyParams
// type ChildFunArgs = [number, string]
You can verify that ChildFunArgs
is the type you expect. At runtime, the emitted code is essentially
const dummyParams = ((x => () => x))(null)();
which has the same effect as
const dummyParams = null;
So there is runtime impact but it is minimal.
How does that ugly stuff work? We are fooling the compiler into thinking we have a function like this:
declare function params<A extends any[]>(f: (...args: A)=>any): () => A;
This hypothetical params()
function would accept a function argument f
, and return a new function of zero arguments which returns a tuple whose type is f
's parameter types. Hmm, that's confusing. What I mean is that params((x: string, y: boolean)=>123)
would return a new function of the type () => [string, boolean]
. Such a function is essentially impossible to implement, but we don't have to really implement it.
Then we pretend we have a value of the type of the fun
method of Parent
:
declare const parentFun: Parent['fun'];
And we call params
on it:
const paramsParentFun = params(parentFun);
// const paramsParentFun: <T>() => [T, string]
Due to recent improvements in higher-order function handling, the compiler infers paramsParentFun
to be of type <T>() => [T, string]
. Now you can pretend to call this function and manually specify number
for T
:
const dummyParams = paramsParentFun<number>();
// const dummyParams: [number, string]
And voila! You get what you're looking for.
The shorter code above is the same as this but it uses type assertions to lie to the compiler about the existence of these other functions and values, so that the emitted JavaScript doesn't have extra mess in it.
Okay, hope that helps. Good luck!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With