Yup schemas for signup forms with conditional rules: working versions (tested 2026-10-15)
Build a Yup v1 signup schema with refs for password confirmation, when() conditional fields, transforms and InferType, collecting every error per field.
passed4 passed · 0 failed2026-10-15 17:11 UTC- Status
- passed
- Last passed
- 2026-10-15 17:11 UTC
- Tests
- 4 / 4
- Language
- TypeScript
- Runtime
- node v22.23.3
pinned versions3
yup@1.7.1@types/node@26.6.5tsx@4.23.15
run it
npm install --no-audit --no-fund --ignore-scripts=false npm test
test environment
node v22.23.3 linux 6.17.0-1022-azure x64 runner: github-actions 38775053802
The code
Exactly the files that passed. Your agent can pull them with get_recipe("yup-form-schemas") (install), or see them on GitHub.
package.json
{
"name": "yup-form-schemas",
"private": true,
"type": "module",
"scripts": {
"test": "node --import tsx --test test/*.test.ts"
},
"dependencies": {
"yup": "1.7.1"
},
"devDependencies": {
"@types/node": "26.6.5",
"tsx": "4.23.15"
}
}
src/signup.ts
import * as yup from "yup";
export const signupSchema = yup.object({
email: yup.string().trim().lowercase().email("invalid email").required("email is required"),
password: yup.string().min(8, "password must be at least 8 characters").required(),
confirmPassword: yup
.string()
.oneOf([yup.ref("password")], "passwords must match")
.required(),
accountType: yup.string().oneOf(["personal", "business"] as const).default("personal"),
company: yup.string().when("accountType", {
is: "business",
then: (s) => s.required("company is required for business accounts"),
otherwise: (s) => s.strip(),
}),
age: yup.number().transform((v, orig) => (orig === "" ? undefined : v)).integer().min(16).optional(),
tags: yup.array(yup.string().required()).max(3).default([]),
});
export type Signup = yup.InferType<typeof signupSchema>;
export type FieldErrors = Record<string, string[]>;
export async function validateSignup(input: unknown): Promise<{ ok: true; value: Signup } | { ok: false; errors: FieldErrors }> {
try {
const value = await signupSchema.validate(input, { abortEarly: false, stripUnknown: true });
return { ok: true, value };
} catch (err) {
if (!(err instanceof yup.ValidationError)) throw err;
const errors: FieldErrors = {};
for (const e of err.inner) {
const key = e.path ?? "_";
(errors[key] ??= []).push(e.message);
}
return { ok: false, errors };
}
}
test/signup.test.ts
import { test } from "node:test";
import assert from "node:assert/strict";
import { signupSchema, validateSignup } from "../src/signup.ts";
const base = { email: " [email protected] ", password: "s3cretpass", confirmPassword: "s3cretpass" };
test("personal signup applies transforms and defaults and strips company", async () => {
const r = await validateSignup({ ...base, company: "ignored", age: "" , extra: 1 });
assert.equal(r.ok, true);
if (!r.ok) return;
assert.deepEqual(r.value, { email: "[email protected]", password: "s3cretpass", confirmPassword: "s3cretpass", accountType: "personal", tags: [] });
});
test("business accounts require a company", async () => {
const r = await validateSignup({ ...base, accountType: "business" });
assert.equal(r.ok, false);
if (r.ok) return;
assert.deepEqual(r.errors.company, ["company is required for business accounts"]);
});
test("collects all errors per field", async () => {
const r = await validateSignup({ email: "x", password: "short", confirmPassword: "other", age: 12, tags: ["a", "b", "c", "d"] });
assert.equal(r.ok, false);
if (r.ok) return;
assert.deepEqual(Object.keys(r.errors).sort(), ["age", "confirmPassword", "email", "password", "tags"]);
assert.deepEqual(r.errors.confirmPassword, ["passwords must match"]);
});
test("isValidSync and cast work without async", () => {
assert.equal(signupSchema.isValidSync({ ...base, age: 30 }), true);
const cast = signupSchema.cast({ ...base, age: "42" });
assert.equal(cast.age, 42);
});
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.