Form-Safe Parsing
Problem
Form submissions often arrive as strings, optional fields, and checkboxes. You need typed output without repeating conversion code for every field.
Solution
Use coercion and wrappers to normalize the form payload in one step, then read messages directly by path.
ts
import { SpellValidationError, s } from '@vielzeug/spell';
const SignupForm = s.object({
age: s.coerce.number().int().min(18),
email: s.string().email(),
marketing: s.coerce.boolean().default(false),
referralCode: s.string().trim().optional().nullable().required(),
});
const result = SignupForm.safeParse({
age: '29',
email: 'ada@example.com',
marketing: 'true',
referralCode: null,
});
if (result.success) {
console.log(result.data.referralCode);
} else if (result.error instanceof SpellValidationError) {
console.log(result.error.messagesAt('email'));
}Pitfalls
.required()removesundefinedbut keepsnull. That matters after.optional().nullable()chains.s.coerce.boolean()is for loosely typed input at the boundary. Keep your app state typed after parsing.- Call
safeParseAsync()if any nested field usescheckAsync().