domains/User.tsimport * as Either from 'fp-ts/Either'; import type {Opaque} from 'type-fest'; import {z} from 'zod'; // Validation rules export const userValidationSchema = z.object({ id: z.string().uuid(), fullName: z.string().min(2).trim(), email: z.string().email(), emailVerifiedAt: z.date(), createdAt: z.date(), updatedAt: z.date(), }); // Opaque types are used to prevent accidental type coercion export type User = Opaque<z.infer<typeof userValidationSchema>, 'User'>; // 1. Parse don't validate // 2. Handle error as an Either.left export function parseUser(value: unknown): Either.Either<Error, User> { return Either.tryCatch( () => userValidationSchema.parse(value) as User, (error) => new Error(error.message) ); } Back to Snippets