Yup is a schema builder for runtime value parsing and validation. Define a schema, transform a value to match, assert the shape of an existing value, or both. Yup schema are extremely expressive and allow modeling complex, interdependent validations, or value transformation.
You are viewing docs for the v1.0.0 of yup, pre-v1 docs are available: here
Killer Features:
- Concise yet expressive schema interface, equipped to model simple to complex data models
- Powerful TypeScript support. Infer static types from schema, or ensure schema correctly implement a type
- Built-in async validation support. Model server-side and client-side validation equally well
- Extensible: add your own type-safe methods and schema
- Rich error details, make debugging a breeze
- Compatible with Standard Schema
Schema are comprised of parsing actions (transforms) as well as assertions (tests) about the input value. Validate an input value to parse it and run the configured set of assertions. Chain together methods to build a schema.
import { object, string, number, date, InferType } from 'yup';
let userSchema = object({
name: string().required(),
age: number().required().positive().integer(),
email: string().email(),
website: string().url().nullable(),
createdOn: date().default(() => new Date()),
});
// parse and assert validity
let user = await userSchema.validate(await fetchUser());
type User = InferType<typeof userSchema>;
/* {
name: string;
age: number;
email?: string | undefined
website?: string | null | undefined
createdOn: Date
}*/Use a schema to coerce or "cast" an input value into the correct type, and optionally transform that value into more concrete and specific values, without making further assertions.
// Attempts to coerce values to the correct type
let parsedUser = userSchema.cast({
name: 'jimmy',
age: '24',
createdOn: '2014-09-23T19:25:25Z',
});
// ✅ { name: 'jimmy', age: 24, createdOn: Date }Know that your input value is already parsed? You can "strictly" validate an input, and avoid the overhead of running parsing logic.
// ❌ ValidationError "age is not a number"
let parsedUser = await userSchema.validate(
{
name: 'jimmy',
age: '24',
},
{ strict: true },
);- Schema basics
- TypeScript integration
- Error message customization
- Standard Schema Support
- API
yupreach(schema: Schema, path: string, value?: object, context?: object): SchemaaddMethod(schemaType: Schema, name: string, method: ()=> Schema): voidref(path: string, options: { contextPrefix: string }): Reflazy((value: any) => Schema): LazyValidationError(errors: string | Array<string>, value: any, path: string)
SchemaSchema.clone(): SchemaSchema.label(label: string): SchemaSchema.meta(metadata: SchemaMetadata): SchemaSchema.describe(options?: ResolveOptions): SchemaDescriptionSchema.concat(schema: Schema): SchemaSchema.validate(value: any, options?: object): Promise<InferType<Schema>, ValidationError>Schema.validateSync(value: any, options?: object): InferType<Schema>Schema.validateAt(path: string, value: any, options?: object): Promise<InferType<Schema>, ValidationError>Schema.validateSyncAt(path: string, value: any, options?: object): InferType<Schema>Schema.isValid(value: any, options?: object): Promise<boolean>Schema.isValidSync(value: any, options?: object): booleanSchema.cast(value: any, options = {}): InferType<Schema>Schema.isType(value: any): value is InferType<Schema>Schema.strict(enabled: boolean = false): SchemaSchema.strip(enabled: boolean = true): SchemaSchema.withMutation(builder: (current: Schema) => void): voidSchema.default(value: any): SchemaSchema.getDefault(options?: object): AnySchema.nullable(message?: string | function): SchemaSchema.nonNullable(message?: string | function): SchemaSchema.defined(): SchemaSchema.optional(): SchemaSchema.required(message?: string | function): SchemaSchema.notRequired(): SchemaSchema.typeError(message: string): SchemaSchema.oneOf(arrayOfValues: Array<any>, message?: string | function): SchemaAlias:equalsSchema.notOneOf(arrayOfValues: Array<any>, message?: string | function)Schema.when(keys: string | string[], builder: object | (values: any[], schema) => Schema): SchemaSchema.test(name: string, message: string | function | any, test: function): SchemaSchema.test(options: object): SchemaSchema.transform((currentValue: any, originalValue: any, schema: Schema, options: object) => any): Schema
- mixed
- string
string.required(message?: string | function): Schemastring.length(limit: number | Ref, message?: string | function): Schemastring.min(limit: number | Ref, message?: string | function): Schemastring.max(limit: number | Ref, message?: string | function): Schemastring.matches(regex: Regex, message?: string | function): Schemastring.matches(regex: Regex, options: { message: string, excludeEmptyString: bool }): Schemastring.email(message?: string | function): Schemastring.url(message?: string | function): Schemastring.uuid(message?: string | function): Schemastring.datetime(options?: {message?: string | function, allowOffset?: boolean, precision?: number})string.datetime(message?: string | function)string.ensure(): Schemastring.trim(message?: string | function): Schemastring.lowercase(message?: string | function): Schemastring.uppercase(message?: string | function): Schema
- number
number.min(limit: number | Ref, message?: string | function): Schemanumber.max(limit: number | Ref, message?: string | function): Schemanumber.lessThan(max: number | Ref, message?: string | function): Schemanumber.moreThan(min: number | Ref, message?: string | function): Schemanumber.positive(message?: string | function): Schemanumber.negative(message?: string | function): Schemanumber.integer(message?: string | function): Schemanumber.truncate(): Schemanumber.round(type: 'floor' | 'ceil' | 'trunc' | 'round' = 'round'): Schema
- boolean
- date
- array
array.of(type: Schema): thisarray.json(): thisarray.length(length: number | Ref, message?: string | function): thisarray.min(limit: number | Ref, message?: string | function): thisarray.max(limit: number | Ref, message?: string | function): thisarray.ensure(): thisarray.compact(rejector: (value) => boolean): Schema
- tuple
- object
- Object schema defaults
object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schemaobject.json(): thisobject.concat(schemaB: ObjectSchema): ObjectSchemaobject.pick(keys: string[]): Schemaobject.omit(keys: string[]): Schemaobject.from(fromKey: string, toKey: string, alias: boolean = false): thisobject.exact(message?: string | function): Schemaobject.stripUnknown(): Schemaobject.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schemaobject.camelCase(): Schemaobject.constantCase(): Schema
Schema definitions, are comprised of parsing "transforms" which manipulate inputs into the desired shape and type, "tests", which make assertions over parsed data. Schema also store a bunch of "metadata", details about the schema itself, which can be used to improve error messages, build tools that dynamically consume schema, or serialize schema into another format.
In order to be maximally flexible yup allows running both parsing and assertions separately to match specific needs
Each built-in type implements basic type parsing, which comes in handy when parsing serialized data, such as JSON. Additionally types implement type specific transforms that can be enabled.
let num = number().cast('1'); // 1
let obj = object({
firstName: string().lowercase().trim(),
})
.json()
.camelCase()
.cast('{"first_name": "jAnE "}'); // { firstName: 'jane' }Custom transforms can be added
let reversedString = string()
.transform((currentValue) => currentValue.split('').reverse().join(''))
.cast('dlrow olleh'); // "hello world"Transforms form a "pipeline", where the value of a previous transform is piped into the next one.
When an input value is undefined yup will apply the schema default if it's configured.
Watch out! values are not guaranteed to be valid types in transform functions. Previous transforms may have failed. For example a number transform may be receive the input value,
NaN, or a number.
Yup schema run "tests" over input values. Tests assert that inputs conform to some criteria. Tests are distinct from transforms, in that they do not change or alter the input (or its type) and are usually reserved for checks that are hard, if not impossible, to represent in static types.
string()
.min(3, 'must be at least 3 characters long')
.email('must be a valid email')
.validate('no'); // ValidationErrorAs with transforms, tests can be customized on the fly
let jamesSchema = string().test(
'is-james',
(d) => `${d.path} is not James`,
(value) => value == null || value === 'James',
);
jamesSchema.validateSync('James'); // "James"
jamesSchema.validateSync('Jane'); // ValidationError "this is not James"Heads up: unlike transforms,
valuein a custom test is guaranteed to be the correct type (in this case an optional string). It still may beundefinedornulldepending on your schema in those cases, you may want to returntruefor absent values unless your transform makes presence related assertions. The test optionskipAbsentwill do this for you if set.
In the simplest case a test function returns true or false depending on the whether the check
passed. In the case of a failing test, yup will throw
a ValidationError with your (or the default)
message for that test. ValidationErrors also contain a bunch of other metadata about the test,
including it's name, what arguments (if any) it was called with, and the path to the failing field
in the case of a nested validation.
Error messages can also be constructed on the fly to customize how the schema fails.
let order = object({
no: number().required(),
sku: string().test({
name: 'is-sku',
skipAbsent: true,
test(value, ctx) {
if (!value.startsWith('s-')) {
return ctx.createError({ message: 'SKU missing correct prefix' });
}
if (!value.endsWith('-42a')) {
return ctx.createError({ message: 'SKU missing correct suffix' });
}
if (value.length < 10) {
return ctx.createError({ message: 'SKU is not the right length' });
}
return true;
},
}),
});
order.validate({ no: 1234, sku: 's-1a45-14a' });Schema are immutable, each method call returns a new schema object. Reuse and pass them around without fear of mutating another instance.
let optionalString = string().optional();
let definedString = optionalString.defined();
let value = undefined;
optionalString.isValid(value); // true
definedString.isValid(value); // falseYup schema produce static TypeScript interfaces. Use InferType to extract that interface:
import * as yup from 'yup';
let personSchema = yup.object({
firstName: yup.string().defined(),
nickName: yup.string().default('').nullable(),
sex: yup
.mixed()
.oneOf(['male', 'female', 'other'] as const)
.defined(),
email: yup.string().nullable().email(),
birthDate: yup.date().nullable().min(new Date(1900, 0, 1)),
});
interface Person extends yup.InferType<typeof personSchema> {
// using interface instead of type generally gives nicer editor feedback
}A schema's default is used when casting produces an undefined output value. Because of this,
setting a default affects the output type of the schema, essentially marking it as "defined()".
import { string } from 'yup';
let value: string = string().default('hi').validate(undefined);
// vs
let value: string | undefined = string().validate(undefined);In some cases a TypeScript type already exists, and you want to ensure that your schema produces a compatible type:
import { object, number, string, ObjectSchema } from 'yup';
interface Person {
name: string;
age?: number;
sex: 'male' | 'female' | 'other' | null;
}
// will raise a compile-time type error if the schema does not produce a valid Person
let schema: ObjectSchema<Person> = object({
name: string().defined(),
age: number().optional(),
sex: string<'male' | 'female' | 'other'>().nullable().defined(),
});
// ❌ errors:
// "Type 'number | undefined' is not assignable to type 'string'."
let badSchema: ObjectSchema<Person> = object({
name: number(),
});You can use TypeScript's interface merging behavior to extend the schema types
if needed. Type extensions should go in an "ambient" type definition file such as your
globals.d.ts. Remember to actually extend the yup type in your application code!
Watch out! merging only works if the type definition is exactly the same, including generics. Consult the yup source code for each type to en