'Why does Zod make all my schema fields optional?
I am using Zod inside my Express & TypeScript & Mongoose API project and when trying to validate my user input against the user schema it returns types conflicts:
Argument of type '{ firstName?: string; lastName?: string; password?: string; passwordConfirmation?: string; email?: string; }' is not assignable to parameter of type 'UserInput'.
Property 'email' is optional in type '{ firstName?: string; lastName?: string; password?: string; passwordConfirmation?: string; email?: string; }' but required in type 'UserInput'
Here is the schema def:
export const createUserSchema = object({
body: object({
firstName: string({
required_error: 'First name is required',
}),
lastName: string({
required_error: 'Last name is required',
}).nonempty(),
password: string({
required_error: 'Password is required',
})
.nonempty()
.min(6, 'Password too short - should be 6 chars minimum'),
passwordConfirmation: string({
required_error: 'Confirm password is required',
}),
email: string({
required_error: 'Email is required',
})
.email('Not a valid email')
.nonempty(),
}).refine((data) => data.password === data.passwordConfirmation, {
message: 'Passwords do not match',
path: ['passwordConfirmation'],
}),
});
export type CreateUserInput = Omit<TypeOf<typeof createUserSchema>, 'body.passwordConfirmation'>;
export interface UserInput {
email: string;
firstName: string;
lastName: string;
password: string;
}
How to make these Zod schema fields all not optional as it is making it optional by default?
Solution 1:[1]
This might be caused by not using strict: true
in TypeScript compiler options as referred to installation section in README file.
Following basic tsconfig.json file would fix that:
{
"compilerOptions": {
"strict": true
}
}
Solution 2:[2]
I suggest to create a basic version and then extend it with 'passwordConfirmation' and relative refinement. Also is important to 'infer' from zod to typescript, to make available typescript linting.
import { object, string, z } from 'zod';
export const userSchema = object({
firstName: string({
required_error: 'First name is required',
}),
lastName: string({
required_error: 'Last name is required',
}).nonempty(),
password: string({
required_error: 'Password is required',
})
.nonempty()
.min(6, 'Password too short - should be 6 chars minimum'),
email: string({
required_error: 'Email is required',
})
.email('Not a valid email')
.nonempty(),
});
type userSchema = z.infer<typeof userSchema>;
export const createUserSchema = object({
body: userSchema
.extend({
passwordConfirmation: string({
required_error: 'Confirm password is required',
}),
})
.refine((data) => data.password === data.passwordConfirmation, {
message: 'Passwords do not match',
path: ['passwordConfirmation'],
}),
});
type createUserSchema = z.infer<typeof createUserSchema>;
const us: userSchema = {
email: '',
firstName: '',
lastName: '',
password: '',
};
const cui: createUserSchema = {
body: {
email: '',
firstName: '',
lastName: '',
password: '',
passwordConfirmation: '',
},
};
Solution 3:[3]
You could still use the Generic Type Required
to mark the object as required:
export type X = Required<CreateUserInput>
You may still want to do this to the sub-objects of your schema if you have, for example, params, body, and query:
export type X = Required<CreateUserInput['body']>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | pr0gramist |
Solution 2 | dev_hero |
Solution 3 | decoder |