Reputation: 912
Having
const a = {
propOne: {
methodOne(arg1, arg2: number) {
console.info('hello...')
}
},
proptwo: {
b(fn, arg2) {
fn('methodOne', 'hello') // Error
fn('methodOne', 1) // OK
}
}
}
I'm trying to make this implementation work where I get the type Error and Intelisense when trying to use fn, in my b method. So basically fn first argument is one of the keys of propOne, and fn second argument is based on which method you selected. Since in the example I selected fn with 'methodOne' the second argument is then inferred to be number since methodOne's arg2 is of type number.
I was able to make this work if I write the types for propOne methods, but I wanted to know if there is a better way to approach this.
Upvotes: 1
Views: 66
Reputation: 250366
You can get this to mostly work, except for what I think is a bug that we will get to later.
The first problem is we will need to use a function in order to help with out inference. Simple variables can have their type inferred from initialization expression or specified in a type annotation, for more complex behavior we need to use the inference behavior of functions.
We will use two type parameters, one for propOne
and one for propTwo
(which I have renamed as actions
and mutations
as we discussed on twitter, the original names were just stand-ins for Vue store props)
Once we have the type parameters captured, it is just a matter of transforming the type parameter for actions
into a suitable parameter for the function
type Func = (...a: any[]) => void;
type SkipFirstParameter<T extends Func> = T extends (a: any, ... r: infer P) => any ? P : [];
type Actions<M extends Record<string, Func>> = <MK extends keyof M>(mutation: MK, ...a: SkipFirstParameter<M[MK]>) => void
function create<T, M extends Record<string, Func>, A extends Record<string, (f: Actions<M>, ...a: any[]) => void>>(o: {
mutations: M
actions: A
}) {
return o
}
const a = create({
mutations: {
methodOne: (arg1: number, arg2: number) => {
console.info('hello...')
},
},
actions: {
b: (fn, arg2: number) => {
fn("methodOne", arg2)
fn("methodOne", "arg2")
}
}
});
You will notice I switched to arrow functions, instead of shorthand syntax or function
expression. This is because inference behaves differently between arrow functions and the others. I believe this to be a compiler bug and field one here but if we use method of function syntax M
will be inferred as unknown
. This probably has something to do with the way TS tries to infer this
for such functions, but the difference in behavior is probably a bug.
Upvotes: 1