I'm encountering a problem with Typescript regarding the inference of types in a generic function.
Here's a simplified version of the code:
type FnObj<I extends z.ZodType> = {
input: I,
func: (input: I extends z.ZodType<infer U> ? U : never) => void
}
type Objs<I extends z.ZodType = z.ZodType> = {
[key: string]: FnObj<I>
}
function createFormDef<FF extends Objs>(formObject: FF) {
return formObject;
}
createFormDef({
test: {
input: z.object({
d: z.number(),
}),
func: (input) => {
input.dsf // This line is not throwing an error, although it should be typed as {d: number}.
},
},
});
I've tried remove generic of Objs to make the single key its own type. But it didn't work. as follows: (also removed the zod part)
type FnObj<I extends any = any> = {
input: I,
func: (input: I) => void
}
type Objs = {
[key: string]: FnObj
}
function createFormDef(formObject: Objs) {
return formObject;
}
createFormDef({
test: {
input: {
hello: 10
},
func: (input) => {
input.dsf // still not working, should typed as {hello:number}
},
},
});
TypeScript doesn't support existentially quantified generics, so you can't say "an object whose property values are
FnObj<I>for someII don't care about". WritingFnObj<any>(which is what yourFnObjbecomes with your default type argument) doesn't accomplish that, because theanytype is not safe, and soFnObj<any>allows things whereinputandfuncare completely unrelated.Instead of trying to write such a type, it would be better to say "an object whose property values at key
KareFnObj<T[K]>for aTthat I specify". That is, you would map over an object typeTto get a correspondingObjs<T>:Now the compiler can infer
Tfrom theformObjectinput:Here
Tis inferred as{test: {hello: number}}, and thusxisObjs<T>which is{test: FnObj<{hello: number}>}, and thus the compiler can contextually type theinputparameter to thefncallback as{hello: number}.Playground link to code