I’m using the zod
library for schema validation and have defined two schemas: FormFactorDevice
and FormFactor
. I’ve also implemented a selectFormFactorDevice
function to select a value from the FormFactorDevice
object based on a given deviceType
.
However, the return value of selectFormFactorDevice
is inferred as any
type, whereas I expect it to be string | undefined
since I’m using z.string()
as the fieldSchema
parameter.
Here’s the code:
export const FormFactorDevice = <T extends z.ZodTypeAny>(fieldSchema: T) =>
z.object({
all: z.optional(fieldSchema),
phone: z.optional(fieldSchema),
smalltablet: z.optional(fieldSchema),
tablet: z.optional(fieldSchema),
laptop: z.optional(fieldSchema),
desktop: z.optional(fieldSchema),
});
export const FormFactor = <Z extends z.ZodTypeAny>(fieldSchema: Z) =>
z.object({
isFormFactor: z.literal(true).default(true),
all: z.optional(fieldSchema),
landscape: z.optional(FormFactorDevice(fieldSchema)),
portrait: z.optional(FormFactorDevice(fieldSchema)),
});
// FormFactorDevice could receive very complicated schema
const b = FormFactorDevice(z.string()).parse({
all: '100',
smalltablet: '200',
phone: '300',
});
export const selectFormFactorDevice = <T extends z.ZodTypeAny>(
formFactorDevice: z.infer<ReturnType<typeof FormFactorDevice<T>>>,
deviceType: 'all' | 'phone' | 'smalltablet' | 'tablet' | 'laptop' | 'desktop'
): z.infer<T> | undefined => {
return formFactorDevice[deviceType];
};
const d = selectFormFactorDevice(b, 'all'); // d is inferred as `any` type
How can I modify the selectFormFactorDevice
function to return the correct type (string | undefined
in this case)?
Note: I’ve tried using z.infer and ReturnType<typeof FormFactorDevice> to infer the correct type, but it doesn’t seem to work. The return value of selectFormFactorDevice is inferred as any type, whereas I expect it to be string | undefined since I’m using z.string() as the fieldSchema parameter.
I know selectFormFactorDevice<z.ZodString>(b, 'all')
should work but I want my function to be smart enough to infer the type automatically
You can try copy and paste the code to zod playground here: https://stackblitz.com/edit/typescript-rqwgxo?file=index.ts
2
Answers
Note: This answer assumes that the TS
strictNullChecks
compiler option is set totrue
(which is not the case for your StackBlitz link).The
selectFormFactorDevice
function has a very simple implementation (it is simply a property access). I assume you want the typings for it to do 2 things:FormFactorDevice
function.I think these typings will fulfill both of those requirements at least somewhat well:
The constraint on
TFormFactorDevice
expresses point 1 (although it may do so in an incomplete manner; I am no expert in the Zod typings). Using a generic parameterTDeviceType
fordeviceType
ensures that the return type is correctly inferred as well (point 2); it simply becomesTFormFactorDevice[TDeviceType]
.If you don’t want to type out all the string options for
TDeviceType
, you can also use thekeyof
type operator like this:TDeviceType extends keyof TFormFactorDevice
.To ensure that the
selectFormFactorDevice
function correctly infers the return type asstring | undefined
, you need to adjust the generic type inference. Currently, TypeScript struggles to infer the correct type due to the complexity of your generic type parameterT
.You can achieve the desired inference by making slight adjustments to your function signature. Instead of directly passing the
fieldSchema
parameter toFormFactorDevice
, you can explicitly specify the type for each form factor inFormFactorDevice
. Here’s how you can modify your code:By explicitly specifying the type for each form factor in
FormFactorDevice
, TypeScript can better infer the return type ofselectFormFactorDevice
. Now, theselectFormFactorDevice
function correctly infers the return type asstring | undefined
based on the providedfieldSchema
.