- Notifications
You must be signed in to change notification settings - Fork936
Dead simple Object schema validation
License
jquense/yup
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
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
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';letuserSchema=object({name:string().required(),age:number().required().positive().integer(),email:string().email(),website:string().url().nullable(),createdOn:date().default(()=>newDate()),});// parse and assert validityletuser=awaituserSchema.validate(awaitfetchUser());typeUser=InferType<typeofuserSchema>;/* { 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 optionallytransform that value into more concrete and specific values, without making further assertions.
// Attempts to coerce values to the correct typeletparsedUser=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 overheadof running parsing logic.
// ❌ ValidationError "age is not a number"letparsedUser=awaituserSchema.validate({name:'jimmy',age:'24',},{strict:true},);
- Schema basics
- TypeScript integration
- Error message customization
- API
yup
reach(schema: Schema, path: string, value?: object, context?: object): Schema
addMethod(schemaType: Schema, name: string, method: ()=> Schema): void
ref(path: string, options: { contextPrefix: string }): Ref
lazy((value: any) => Schema): Lazy
ValidationError(errors: string | Array<string>, value: any, path: string)
Schema
Schema.clone(): Schema
Schema.label(label: string): Schema
Schema.meta(metadata: SchemaMetadata): Schema
Schema.describe(options?: ResolveOptions): SchemaDescription
Schema.concat(schema: Schema): Schema
Schema.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): boolean
Schema.cast(value: any, options = {}): InferType<Schema>
Schema.isType(value: any): value is InferType<Schema>
Schema.strict(enabled: boolean = false): Schema
Schema.strip(enabled: boolean = true): Schema
Schema.withMutation(builder: (current: Schema) => void): void
Schema.default(value: any): Schema
Schema.getDefault(options?: object): Any
Schema.nullable(message?: string | function): Schema
Schema.nonNullable(message?: string | function): Schema
Schema.defined(): Schema
Schema.optional(): Schema
Schema.required(message?: string | function): Schema
Schema.notRequired(): Schema
Schema.typeError(message: string): Schema
Schema.oneOf(arrayOfValues: Array<any>, message?: string | function): Schema
Alias:equals
Schema.notOneOf(arrayOfValues: Array<any>, message?: string | function)
Schema.when(keys: string | string[], builder: object | (values: any[], schema) => Schema): Schema
Schema.test(name: string, message: string | function | any, test: function): Schema
Schema.test(options: object): Schema
Schema.transform((currentValue: any, originalValue: any) => any): Schema
- mixed
- string
string.required(message?: string | function): Schema
string.length(limit: number | Ref, message?: string | function): Schema
string.min(limit: number | Ref, message?: string | function): Schema
string.max(limit: number | Ref, message?: string | function): Schema
string.matches(regex: Regex, message?: string | function): Schema
string.matches(regex: Regex, options: { message: string, excludeEmptyString: bool }): Schema
string.email(message?: string | function): Schema
string.url(message?: string | function): Schema
string.uuid(message?: string | function): Schema
string.datetime(options?: {message?: string | function, allowOffset?: boolean, precision?: number})
string.datetime(message?: string | function)
string.ensure(): Schema
string.trim(message?: string | function): Schema
string.lowercase(message?: string | function): Schema
string.uppercase(message?: string | function): Schema
- number
number.min(limit: number | Ref, message?: string | function): Schema
number.max(limit: number | Ref, message?: string | function): Schema
number.lessThan(max: number | Ref, message?: string | function): Schema
number.moreThan(min: number | Ref, message?: string | function): Schema
number.positive(message?: string | function): Schema
number.negative(message?: string | function): Schema
number.integer(message?: string | function): Schema
number.truncate(): Schema
number.round(type: 'floor' | 'ceil' | 'trunc' | 'round' = 'round'): Schema
- boolean
- date
- array
array.of(type: Schema): this
array.json(): this
array.length(length: number | Ref, message?: string | function): this
array.min(limit: number | Ref, message?: string | function): this
array.max(limit: number | Ref, message?: string | function): this
array.ensure(): this
array.compact(rejector: (value) => boolean): Schema
- tuple
- object
- Object schema defaults
object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema
object.json(): this
object.concat(schemaB: ObjectSchema): ObjectSchema
object.pick(keys: string[]): Schema
object.omit(keys: string[]): Schema
object.from(fromKey: string, toKey: string, alias: boolean = false): this
object.exact(message?: string | function): Schema
object.stripUnknown(): Schema
object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema
object.camelCase(): Schema
object.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.
letnum=number().cast('1');// 1letobj=object({firstName:string().lowercase().trim(),}).json().camelCase().cast('{"first_name": "jAnE "}');// { firstName: 'jane' }
Custom transforms can be added
letreversedString=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 isundefined
yup will apply the schema default if it's configured.
Watch out! values are not guaranteed to be valid types in transform functions. Previous transformsmay 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 somecriteria. 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');// ValidationError
As with transforms, tests can be customized on the fly
letjamesSchema=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,
value
in a custom test is guaranteed to be the correct type(in this case an optional string). It still may beundefined
ornull
depending on your schemain those cases, you may want to returntrue
for absent values unless your transform makes presencerelated assertions. The test optionskipAbsent
will do this for you if set.
In the simplest case a test function returnstrue
orfalse
depending on the whether the checkpassed. In the case of a failing test, yup will throwaValidationError
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 fieldin the case of a nested validation.
Error messages can also be constructed on the fly to customize how the schema fails.
letorder=object({no:number().required(),sku:string().test({name:'is-sku',skipAbsent:true,test(value,ctx){if(!value.startsWith('s-')){returnctx.createError({message:'SKU missing correct prefix'});}if(!value.endsWith('-42a')){returnctx.createError({message:'SKU missing correct suffix'});}if(value.length<10){returnctx.createError({message:'SKU is not the right length'});}returntrue;},}),});order.validate({no:1234,sku:'s-1a45-14a'});
Schema are immutable, each method call returns a new schema object. Reuse and pass them around withoutfear of mutating another instance.
letoptionalString=string().optional();letdefinedString=optionalString.defined();letvalue=undefined;optionalString.isValid(value);// truedefinedString.isValid(value);// false
Yup schema produce static TypeScript interfaces. UseInferType
to extract that interface:
import*asyupfrom'yup';letpersonSchema=yup.object({firstName:yup.string().defined(),nickName:yup.string().default('').nullable(),sex:yup.mixed().oneOf(['male','female','other']asconst).defined(),email:yup.string().nullable().email(),birthDate:yup.date().nullable().min(newDate(1900,0,1)),});interfacePersonextendsyup.InferType<typeofpersonSchema>{// using interface instead of type generally gives nicer editor feedback}
A schema's default is used when casting produces anundefined
output value. Because of this,setting a default affects the output type of the schema, essentially marking it as "defined()".
import{string}from'yup';letvalue:string=string().default('hi').validate(undefined);// vsletvalue:string|undefined=string().validate(undefined);
In some cases a TypeScript type already exists, and you want to ensure thatyour schema produces a compatible type:
import{object,number,string,ObjectSchema}from'yup';interfacePerson{name:string;age?:number;sex:'male'|'female'|'other'|null;}// will raise a compile-time type error if the schema does not produce a valid Personletschema: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'."letbadSchema:ObjectSchema<Person>=object({name:number(),});
You can use TypeScript's interface merging behavior to extend the schema typesif needed. Type extensions should go in an "ambient" type definition file such as yourglobals.d.ts
. Remember to actually extend the yup type in your application code!
Watch out! merging only works if the type definition isexactly the same, includinggenerics. Consult the yup source code for each type to ensure you are defining it correctly
// globals.d.tsdeclare module'yup'{interfaceStringSchema<TType,TContext,TDefault,TFlags>{append(appendStr:string): this;}}// app.tsimport{addMethod,string}from'yup';addMethod(string,'append',functionappend(appendStr:string){returnthis.transform((value)=>`${value}${appendStr}`);});string().append('~~~~').cast('hi');// 'hi~~~~'
Youmust have thestrictNullChecks
compiler option enabled for type inference to work.
We also recommend settingsstrictFunctionTypes
tofalse
, for functionally better types. Yesthis reduces overall soundness, however TypeScript already disables this checkfor methods and constructors (note from TS docs):
During development of this feature, we discovered a large number of inherentlyunsafe class hierarchies, including some in the DOM. Because of this,the setting only applies to functions written in function syntax, not to those in method syntax:
Your mileage will vary, but we've found that this check doesn't prevent many ofreal bugs, while increasing the amount of onerous explicit type casting in apps.
Default error messages can be customized for when no message is provided with a validation test.If any message is missing in the custom dictionary the error message will default to Yup's one.
import{setLocale}from'yup';setLocale({mixed:{default:'Não é válido',},number:{min:'Deve ser maior que ${min}',},});// now use Yup schemas AFTER you defined your custom dictionaryletschema=yup.object().shape({name:yup.string(),age:yup.number().min(18),});try{awaitschema.validate({name:'jimmy',age:11});}catch(err){err.name;// => 'ValidationError'err.errors;// => ['Deve ser maior que 18']}
If you need multi-language support, yup has got you covered. The functionsetLocale
accepts functions that can be used togenerate error objects with translation keys and values. These can be fed it into your favorite i18n library.
import{setLocale}from'yup';setLocale({// use constant translation keys for messages without valuesmixed:{default:'field_invalid',},// use functions to generate an error object that includes the value from the schemanumber:{min:({ min})=>({key:'field_too_short',values:{ min}}),max:({ max})=>({key:'field_too_big',values:{ max}}),},});// ...letschema=yup.object().shape({name:yup.string(),age:yup.number().min(18),});try{awaitschema.validate({name:'jimmy',age:11});}catch(err){messages=err.errors.map((err)=>i18next.t(err.key));}
The module export.
// core schemaimport{mixed,string,number,boolean,bool,date,object,array,ref,lazy,}from'yup';// Classesimport{Schema,MixedSchema,StringSchema,NumberSchema,BooleanSchema,DateSchema,ArraySchema,ObjectSchema,}from'yup';// Typesimporttype{InferType,ISchema,AnySchema,AnyObjectSchema}from'yup';
For nested schemas,reach
will retrieve an inner schema based on the provided path.
For nested schemas that need to resolve dynamically, you can provide avalue
and optionallyacontext
object.
import{reach}from'yup';letschema=object({nested:object({arr:array(object({num:number().max(4)})),}),});reach(schema,'nested.arr.num');reach(schema,'nested.arr[].num');reach(schema,'nested.arr[1].num');reach(schema,'nested["arr"][1].num');
Adds a new method to the core schema types. A friendlier convenience method forschemaType.prototype[name] = method
.
import{addMethod,date}from'yup';addMethod(date,'format',functionformat(formats,parseStrict){returnthis.transform((value,originalValue,ctx)=>{if(ctx.isType(value))returnvalue;value=Moment(originalValue,formats,parseStrict);returnvalue.isValid() ?value.toDate() :newDate('');});});
If you want to add a method to ALL schema types, extend the abstract base class:Schema
import{addMethod,Schema}from'yup';addMethod(Schema,'myMethod', ...)
Creates a reference to another sibling or sibling descendant field. Refs are resolvedatvalidation/cast time and supported where specified. Refs are evaluated in the proper order so thatthe ref value is resolved before the field using the ref (be careful of circular dependencies!).
import{ref,object,string}from'yup';letschema=object({baz:ref('foo.bar'),foo:object({bar:string(),}),x:ref('$x'),});schema.cast({foo:{bar:'boom'}},{context:{x:5}});// => { baz: 'boom', x: 5, foo: { bar: 'boom' }}
Creates a schema that is evaluated at validation/cast time. Useful for creatingrecursive schema like Trees, for polymorphic fields and arrays.
CAUTION! When defining parent-child recursive object schema, you want to reset thedefault()
tonull
on the child—otherwise the object will infinitely nest itself when you cast it!
letnode=object({id:number(),child:yup.lazy(()=>node.default(undefined)),});letrenderable=yup.lazy((value)=>{switch(typeofvalue){case'number':returnnumber();case'string':returnstring();default:returnmixed();}});letrenderables=array().of(renderable);
Thrown on failed validations, with the following properties
name
: "ValidationError"type
: the specific test type or test "name", that failed.value
: The field value that was tested;params
?: The test inputs, such as max value, regex, etc;path
: a string, indicating where there error was thrown.path
is empty at the root level.errors
: array of error messagesinner
: in the case of aggregate errors, inner is an array ofValidationErrors
throw earlier in thevalidation chain. When theabortEarly
option isfalse
this is where you can inspect each error thrown,alternatively,errors
will have all of the messages from each inner error.
Schema
is the abstract base class that all schema type inherit from. It provides a number of base methods and propertiesto all other schema types.
Note: unless you are creating a custom schema type, Schema should never be used directly. For unknown/any types use
mixed()
Creates a deep copy of the schema. Clone is used internally to return a new schema with every schema state change.
Overrides the key name which is used in error messages.
Adds to a metadata object, useful for storing data with a schema, that doesn't belongto the cast object itself.
A customSchemaMetadata
interface can be defined throughmergingwith theCustomSchemaMetadata
interface. Start by creating ayup.d.ts
filein your package and creating your desiredCustomSchemaMetadata
interface:
// yup.d.tsimport'yup';declare module'yup'{// Define your desired `SchemaMetadata` interface by merging the// `CustomSchemaMetadata` interface.exportinterfaceCustomSchemaMetadata{placeholderText?:string;tooltipText?:string;// …}}
Collects schema details (like meta, labels, and active tests) into a serializabledescription object.
letschema=object({name:string().required(),});letdescription=schema.describe();
For schema with dynamic components (references, lazy, or conditions), describe requiresmore context to accurately return the schema description. In these cases provideoptions
import{ref,object,string,boolean}from'yup';letschema=object({isBig:boolean(),count:number().when('isBig',{is:true,then:(schema)=>schema.min(5),otherwise:(schema)=>schema.min(0),}),});schema.describe({value:{isBig:true}});
And below are the description types, which differ a bit depending on the schema type.
interfaceSchemaDescription{type:string;label?:string;meta:object|undefined;oneOf:unknown[];notOneOf:unknown[];default?:unknown;nullable:boolean;optional:boolean;tests:Array<{name?:string;params:ExtraParams|undefined}>;// Present on object schema descriptionsfields:Record<string,SchemaFieldDescription>;// Present on array schema descriptionsinnerType?:SchemaFieldDescription;}typeSchemaFieldDescription=|SchemaDescription|SchemaRefDescription|SchemaLazyDescription;interfaceSchemaRefDescription{type:'ref';key:string;}interfaceSchemaLazyDescription{type:string;label?:string;meta:object|undefined;}
Creates a new instance of the schema by combining two schemas. Only schemas of the same type can be concatenated.concat
is not a "merge" function in the sense that all settings from the provided schema, override ones in thebase, including type, presence and nullability.
mixed<string>().defined().concat(mixed<number>().nullable());// produces the equivalent to:mixed<number>().defined().nullable();
Returns the parses and validates an input value, returning the parsed value or throwing an error. This method isasynchronous and returns a Promise object, that is fulfilled with the value, or rejectedwith aValidationError
.
value=awaitschema.validate({name:'jimmy',age:24});
Provideoptions
to more specifically control the behavior ofvalidate
.
interfaceOptions{// when true, parsing is skipped and the input is validated "as-is" strict:boolean=false;// Throw on the first error or collect and return all abortEarly:boolean=true;// Remove unspecified keys from objects stripUnknown:boolean=false;// when `false` validations will be performed shallowly recursive:boolean=true;// External values that can be provided to validations and conditionalscontext?:object;}
Runs validatations synchronouslyif possible and returns the resulting value,or throws a ValidationError. Accepts all the same options asvalidate
.
Synchronous validation only works if there are no configured async tests, e.g tests that return a Promise.For instance this will work:
letschema=number().test('is-42',"this isn't the number i want",(value)=>value!=42,);schema.validateSync(23);// throws ValidationError
however this will not:
letschema=number().test('is-42',"this isn't the number i want",(value)=>Promise.resolve(value!=42),);schema.validateSync(42);// throws Error
Schema.validateAt(path: string, value: any, options?: object): Promise<InferType<Schema>, ValidationError>
Validate a deeply nested path within the schema. Similar to howreach
works,but uses the resulting schema as the subject for validation.
Note! The
value
here is theroot value relative to the starting schema, not the value at the nested path.
letschema=object({foo:array().of(object({loose:boolean(),bar:string().when('loose',{is:true,otherwise:(schema)=>schema.strict(),}),}),),});letrootValue={foo:[{bar:1},{bar:1,loose:true}],};awaitschema.validateAt('foo[0].bar',rootValue);// => ValidationError: must be a stringawaitschema.validateAt('foo[1].bar',rootValue);// => '1'
Same asvalidateAt
but synchronous.
Returnstrue
when the passed in value matches the schema.isValid
isasynchronous and returns a Promise object.
Takes the same options asvalidate()
.
Synchronously returnstrue
when the passed in value matches the schema.
Takes the same options asvalidateSync()
and has the same caveats around async tests.
Attempts to coerce the passed in value to a value that matches the schema. For example:'5'
willcast to5
when using thenumber()
type. Failed casts generally returnnull
, but may alsoreturn results likeNaN
and unexpected strings.
Provideoptions
to more specifically control the behavior ofvalidate
.
interfaceCastOptions<TContextextends{}>{// Remove undefined properties from objectsstripUnknown:boolean=false;// Throws a TypeError if casting doesn't produce a valid type// note that the TS return type is inaccurate when this is `false`, use with cautionassert?:boolean=true;// External values that used to resolve conditions and referencescontext?:TContext;}
Runs a type check against the passed invalue
. It returns true if it matches,it does not cast the value. Whennullable()
is setnull
is considered a valid value of the type.You should useisType
for all Schema type checks.
Sets thestrict
option totrue
. Strict schemas skip coercion and transformation attempts,validating the value "as is".
Marks a schema to be removed from an output object. Only works as a nested schema.
letschema=object({useThis:number(),notThis:string().strip(),});schema.cast({notThis:'foo',useThis:4});// => { useThis: 4 }
Schema withstrip
enabled have an inferred type ofnever
, allowing them to beremoved from the overall type:
letschema=object({useThis:number(),notThis:string().strip(),});InferType<typeofschema>;/*{ useThis?: number | undefined}*/
First the legally required Rich Hickey quote:
If a tree falls in the woods, does it make a sound?
If a pure function mutates some local data in order to produce an immutable return value, is that ok?
withMutation
allows you to mutate the schema in place, instead of the default behavior which clones before each change. Generally this isn't necessary since the vast majority of schema changes happen during the initialdeclaration, and only happen once over the lifetime of the schema, so performance isn't an issue.However certain mutationsdo occur at cast/validation time, (such as conditional schema usingwhen()
), orwhen instantiating a schema object.
object().shape({key:string()}).withMutation((schema)=>{returnarrayOfObjectTests.forEach((test)=>{schema.test(test);});});
Sets a default value to use when the value isundefined
.Defaults are created after transformations are executed, but before validations, to help ensure that safedefaults are specified. The default value will be cloned on each use, which can incur performance penaltyfor objects and arrays. To avoid this overhead you can also pass a function that returns a new default.Note thatnull
is considered a separate non-empty value.
yup.string.default('nothing');yup.object.default({number:5});// object will be cloned every time a default is neededyup.object.default(()=>({number:5}));// this is cheaperyup.date.default(()=>newDate());// also helpful for defaults that change over time
Retrieve a previously set default value.getDefault
will resolve any conditions that may alter the default. Optionally passoptions
withcontext
(for more info oncontext
seeSchema.validate
).
Indicates thatnull
is a valid value for the schema. Withoutnullable()
null
is treated as a different type and will failSchema.isType()
checks.
letschema=number().nullable();schema.cast(null);// nullInferType<typeofschema>;// number | null
The opposite ofnullable
, removesnull
from valid type values for the schema.Schema are non nullable by default.
letschema=number().nonNullable();schema.cast(null);// TypeErrorInferType<typeofschema>;// number
Require a value for the schema. All field values apart fromundefined
meet this requirement.
letschema=string().defined();schema.cast(undefined);// TypeErrorInferType<typeofschema>;// string
The opposite ofdefined()
allowsundefined
values for the given type.
letschema=string().optional();schema.cast(undefined);// undefinedInferType<typeofschema>;// string | undefined
Mark the schema as required, which will not allowundefined
ornull
as a value.required
negates the effects of callingoptional()
andnullable()
Watch out!
string().required
) works a littledifferent and additionally prevents empty string values (''
) when required.
Mark the schema as not required. This is a shortcut forschema.nullable().optional()
;
Define an error message for failed type checks. The${value}
and${type}
interpolation canbe used in themessage
argument.
Only allow values from set of values. Values added are removed from anynotOneOf
values if present.The${values}
interpolation can be used in themessage
argument. If a ref or refs are provided,the${resolved}
interpolation can be used in the message argument to get the resolved values that were checkedat validation time.
Note thatundefined
does not fail this validator, even whenundefined
is not included inarrayOfValues
.If you don't wantundefined
to be a valid value, you can useSchema.required
.
letschema=yup.mixed().oneOf(['jimmy',42]);awaitschema.isValid(42);// => trueawaitschema.isValid('jimmy');// => trueawaitschema.isValid(newDate());// => false
Disallow values from a set of values. Values added are removed fromoneOf
values if present.The${values}
interpolation can be used in themessage
argument. If a ref or refs are provided,the${resolved}
interpolation can be used in the message argument to get the resolved values that were checkedat validation time.
letschema=yup.mixed().notOneOf(['jimmy',42]);awaitschema.isValid(42);// => falseawaitschema.isValid(newDate());// => true
Adjust the schema based on a sibling or sibling children fields. You can provide an objectliteral where the keyis
is value or a matcher function,then
provides the true schema and/orotherwise
for the failure condition.
is
conditions are strictly compared (===
) if you want to use a different form of equality youcan provide a function like:is: (value) => value == true
.
You can also prefix properties with$
to specify a property that is dependentoncontext
passed in byvalidate()
orcast
instead of the input value.
when
conditions are additive.
then
andotherwise
are specified functions(schema: Schema) => Schema
.
letschema=object({isBig:boolean(),count:number().when('isBig',{is:true,// alternatively: (val) => val == truethen:(schema)=>schema.min(5),otherwise:(schema)=>schema.min(0),}).when('$other',([other],schema)=>other===4 ?schema.max(6) :schema,),});awaitschema.validate(value,{context:{other:4}});
You can also specify more than one dependent key, in which case each value will be spread as an argument.
letschema=object({isSpecial:boolean(),isBig:boolean(),count:number().when(['isBig','isSpecial'],{is:true,// alternatively: (isBig, isSpecial) => isBig && isSpecialthen:(schema)=>schema.min(5),otherwise:(schema)=>schema.min(0),}),});awaitschema.validate({isBig:true,isSpecial:true,count:10,});
Alternatively you can provide a function that returns a schema, called with an array of values for each provided key the current schema.
letschema=yup.object({isBig:yup.boolean(),count:yup.number().when('isBig',([isBig],schema)=>{returnisBig ?schema.min(5) :schema.min(0);}),});awaitschema.validate({isBig:false,count:4});
Adds a test function to the validation chain. Tests are run after any object is cast.Many types have some tests built in, but you can create custom ones easily.In order to allow asynchronous custom validationsall (or no) tests are run asynchronously.A consequence of this is that test execution order cannot be guaranteed.
All tests must provide aname
, an errormessage
and a validation function that must returntrue
when the currentvalue
is valid andfalse
or aValidationError
otherwise.To make a test async return a promise that resolvestrue
orfalse
or aValidationError
.
For themessage
argument you can provide a string which will interpolate certain valuesif specified using the${param}
syntax. By default all test messages are passed apath
valuewhich is valuable in nested schemas.
Thetest
function is called with the currentvalue
. For more advanced validations you canuse the alternate signature to provide more options (see below):
letjimmySchema=string().test('is-jimmy','${path} is not Jimmy',(value,context)=>value==='jimmy',);// or make it async by returning a promiseletasyncJimmySchema=string().label('First name').test('is-jimmy',({ label})=>`${label} is not Jimmy`,// a message can also be a functionasync(value,testContext)=>(awaitfetch('/is-jimmy/'+value)).responseText==='true',);awaitschema.isValid('jimmy');// => trueawaitschema.isValid('john');// => false
Test functions are called with a special context value, as the second argument, that exposes some useful metadataand functions. For non arrow functions, the test context is also set as the functionthis
. Watch out, if you accessit viathis
it won't work in an arrow function.
testContext.path
: the string path of the current validationtestContext.schema
: the resolved schema object that the test is running against.testContext.options
: theoptions
object that validate() or isValid() was called withtestContext.parent
: in the case of nested schema, this is the value of the parent objecttestContext.originalValue
: the original value that is being testedtestContext.createError(Object: { path: String, message: String, params: Object })
: create and return avalidation error. Useful for dynamically setting thepath
,params
, or more likely, the errormessage
.If either option is omitted it will use the current path, or default message.
Alternativetest(..)
signature.options
is an object containing some of the following options:
Options={// unique name identifying the testname:string;// test function, determines schema validity test:(value:any)=>boolean;// the validation error message message:string;// values passed to message for interpolation params: ?object;// mark the test as exclusive, meaning only one test of the same name can be active at once exclusive:boolean=false;}
In the case of mixing exclusive and non-exclusive tests the following logic is used.If a non-exclusive test is added to a schema with an exclusive test of the same namethe exclusive test is removed and further tests of the same name will be stacked.
If an exclusive test is added to a schema with non-exclusive tests of the same namethe previous tests are removed and further tests of the same name will replace each other.
letmax=64;letschema=yup.string().test({name:'max',exclusive:true,params:{ max},message:'${path} must be less than ${max} characters',test:(value)=>value==null||value.length<=max,});
Adds a transformation to the transform chain. Transformations are central to the casting process,default transforms for each type coerce values to the specific type (as verified byisType()
). transforms are run before validations and only applied when the schema is not marked asstrict
(the default). Some types have built in transformations.
Transformations are useful for arbitrarily altering how the object is cast,however, you should take carenot to mutate the passed in value. Transforms are run sequentially so eachvalue
represents thecurrent state of the cast, you can use theoriginalValue
param if you need to work on the raw initial value.
letschema=string().transform((value,originalValue)=>{returnthis.isType(value)&&value!==null ?value.toUpperCase() :value;});schema.cast('jimmy');// => 'JIMMY'
Each types will handle basic coercion of values to the proper type for you, but occasionallyyou may want to adjust or refine the default behavior. For example, if you wanted to use a differentdate parsing strategy than the default one you could do that with a transform.
module.exports=function(formats='MMM dd, yyyy'){returndate().transform((value,originalValue,context)=>{// check to see if the previous transform already parsed the dateif(context.isType(value))returnvalue;// the default coercion failed so let's try it with Moment.js insteadvalue=Moment(originalValue,formats);// if it's valid return the date object, otherwise return an `InvalidDate`returnvalue.isValid() ?value.toDate() :newDate('');});};
Creates a schema that matches all types, or just the ones you configure. Inherits fromSchema
.Mixed types extends{}
by default instead ofany
orunknown
. This is because in TypeScript{}
meansanything that isn'tnull
orundefined
which yup treats distinctly.
import{mixed,InferType}from'yup';letschema=mixed().nullable();schema.validateSync('string');// 'string';schema.validateSync(1);// 1;schema.validateSync(newDate());// Date;InferType<typeofschema>;// {} | undefinedInferType<typeofschema.nullable().defined()>;// {} | null
Custom types can be implemented by passing a typecheck
function. This will alsonarrow the TypeScript type for the schema.
import{mixed,InferType}from'yup';letobjectIdSchema=yup.mixed((input):input isObjectId=>inputinstanceofObjectId).transform((value:any,input,ctx)=>{if(ctx.isType(value))returnvalue;returnnewObjectId(value);});awaitobjectIdSchema.validate(ObjectId('507f1f77bcf86cd799439011'));// ObjectId("507f1f77bcf86cd799439011")awaitobjectIdSchema.validate('507f1f77bcf86cd799439011');// ObjectId("507f1f77bcf86cd799439011")InferType<typeofobjectIdSchema>;// ObjectId
Define a string schema. Inherits fromSchema
.
letschema=yup.string();awaitschema.isValid('hello');// => true
By default, thecast
logic ofstring
is to calltoString
on the value if it exists.
empty values are not coerced (useensure()
to coerce empty values to empty strings).
Failed casts return the input value.
The same as themixed()
schema required,except that empty strings are also considered 'missing' values.
Set a required length for the string value. The${length}
interpolation can be used in themessage
argument
Set a minimum length limit for the string value. The${min}
interpolation can be used in themessage
argument
Set a maximum length limit for the string value. The${max}
interpolation can be used in themessage
argument
Provide an arbitraryregex
to match the value against.
letschema=string().matches(/(hi|bye)/);awaitschema.isValid('hi');// => trueawaitschema.isValid('nope');// => false
An alternate signature forstring.matches
with an options object.excludeEmptyString
, when true,short circuits the regex test when the value is an empty string, making it a easier to avoidmatching nothing without complicating the regex.
letschema=string().matches(/(hi|bye)/,{excludeEmptyString:true});awaitschema.isValid('');// => true
Validates the value as an email address using the same regex as defined by the HTML spec.
WATCH OUT: Validating email addresses is nearly impossible with just code. Differentclients and servers accept different things and many diverge from the various specs defining"valid" emails. The ONLY real way to validate an email address is to send a verification emailto it and check that the user got it. With that in mind, yup picks a relatively simple regexthat does not cover all cases, but aligns with browser input validation behavior since HTMLforms are a common use case for yup.
If you have more specific needs please override the email method with your own logic or regex:
yup.addMethod(yup.string,'email',functionvalidateEmail(message){returnthis.matches(myEmailRegex,{ message,name:'email',excludeEmptyString:true,});});
Validates the value as a valid URL via a regex.
Validates the value as a valid UUID via a regex.
Validates the value as an ISO datetime via a regex. Defaults to UTC validation; timezone offsets are not permitted (seeoptions.allowOffset
).
Unlike.date()
,datetime
will not convert the string to aDate
object.datetime
also provides greater customization over the required format of the datetime string thandate
does.
options.allowOffset
: Allow a time zone offset. False requires UTC 'Z' timezone.(default: false)options.precision
: Require a certain sub-second precision on the date.(default: null -- any (or no) sub-second precision)
An alternate signature forstring.datetime
that can be used when you don't need to pass options other thanmessage
.
Transformsundefined
andnull
values to an empty string along withsetting thedefault
to an empty string.
Transforms string values by removing leading and trailing whitespace. Ifstrict()
is set it will only validate that the value is trimmed.
Transforms the string value to lowercase. Ifstrict()
is set itwill only validate that the value is lowercase.
Transforms the string value to uppercase. Ifstrict()
is set itwill only validate that the value is uppercase.
Define a number schema. Inherits fromSchema
.
letschema=yup.number();awaitschema.isValid(10);// => true
The defaultcast
logic ofnumber
is:parseFloat
.
Failed casts returnNaN
.
Set the minimum value allowed. The${min}
interpolation can be used in themessage
argument.
Set the maximum value allowed. The${max}
interpolation can be used in themessage
argument.
Value must be less thanmax
. The${less}
interpolation can be used in themessage
argument.
Value must be strictly greater thanmin
. The${more}
interpolation can be used in themessage
argument.
Value must be a positive number.
Value must be a negative number.
Validates that a number is an integer.
Transformation that coerces the value to an integer by stripping off the digitsto the right of the decimal point.
Adjusts the value via the specified method ofMath
(defaults to 'round').
Define a boolean schema. Inherits fromSchema
.
letschema=yup.boolean();awaitschema.isValid(true);// => true
Define a Date schema. By default ISO date strings will parse correctly,for more robust parsing options see the extending schema types at the end of the readme.Inherits fromSchema
.
letschema=yup.date();awaitschema.isValid(newDate());// => true
The defaultcast
logic ofdate
is pass the value to theDate
constructor, failing that, it will attemptto parse the date as an ISO date string.
If you would like ISO strings to not be cast to a
Date
object, use.datetime()
instead.
Failed casts return an invalid Date.
Set the minimum date allowed. When a string is provided it will attempt to cast to a date firstand use the result as the limit.
Set the maximum date allowed, When a string is provided it will attempt to cast to a date firstand use the result as the limit.
Define an array schema. Arrays can be typed or not, When specifying the element type,cast
andisValid
will apply to the elements as well. Options passed intoisValid
are also passed to child schemas.
Inherits fromSchema
.
letschema=yup.array().of(yup.number().min(2));awaitschema.isValid([2,3]);// => trueawaitschema.isValid([1,-24]);// => falseschema.cast(['2','3']);// => [2, 3]
You can also pass a subtype schema to the array constructor as a convenience.
array().of(yup.number());// orarray(yup.number());
Arrays have no default casting behavior.
Specify the schema of array elements.of()
is optional and when omitted the array schema willnot validate its contents.
Attempt to parse input string values as JSON usingJSON.parse
.
Set a specific length requirement for the array. The${length}
interpolation can be used in themessage
argument.
Set a minimum length limit for the array. The${min}
interpolation can be used in themessage
argument.
Set a maximum length limit for the array. The${max}
interpolation can be used in themessage
argument.
Ensures that the value is an array, by setting the default to[]
and transformingnull
andundefined
values to an empty array as well. Any non-empty, non-array value will be wrapped in an array.
array().ensure().cast(null);// => []array().ensure().cast(1);// => [1]array().ensure().cast([1]);// => [1]
Removes falsey values from the array. Providing a rejecter function lets you specify the rejection criteria yourself.
array().compact().cast(['',1,0,4,false,null]);// => [1, 4]array().compact(function(v){returnv==null;}).cast(['',1,0,4,false,null]);// => ['', 1, 0, 4, false]
Tuples, are fixed length arrays where each item has a distinct type.
Inherits fromSchema
.
import{tuple,string,number,InferType}from'yup';letschema=tuple([string().label('name'),number().label('age').positive().integer(),]);awaitschema.validate(['James',3]);// ['James', 3]awaitschema.validate(['James',-24]);// => ValidationError: age must be a positive numberInferType<typeofschema>// [string, number] | undefined
tuples have no default casting behavior.
Define an object schema. Options passed intoisValid
are also passed to child schemas.Inherits fromSchema
.
yup.object({name:string().required(),age:number().required().positive().integer(),email:string().email(),website:string().url(),});
object schema do not have any default transforms applied.
Object schema come with a default value already set, which "builds" out the object shape, asets any defaults for fields:
letschema=object({name:string().default(''),});schema.default();// -> { name: '' }
This may be a bit surprising, but is usually helpful since it allows large, nestedschema to create default values that fill out the whole shape and not just the root object. There isone gotcha! though. For nested object schema that are optional but include non optional fieldsmay fail in unexpected ways:
letschema=object({id:string().required(),names:object({first:string().required(),}),});schema.isValid({id:1});// false! names.first is required
This is because yup casts the input object before running validationwhich will produce:
{ id: '1', names: { first: undefined }}
During the validation phasenames
exists, and is validated, findingnames.first
missing.If you wish to avoid this behavior do one of the following:
- Set the nested default to undefined:
names.default(undefined)
- mark it nullable and default to null:
names.nullable().default(null)
Define the keys of the object and the schemas for said keys.
Note that you can chainshape
method, which acts likeObject.assign
.
object({a:string(),b:number(),}).shape({b:string(),c:number(),});
would be exactly the same as:
object({a:string(),b:string(),c:number(),});
Attempt to parse input string values as JSON usingJSON.parse
.
Creates a object schema, by applying all settings and fields fromschemaB
to the base, producing a new schema.The object shape is shallowly merged with common fields fromschemaB
taking precedence over the basefields.
Create a new schema from a subset of the original's fields.
letperson=object({age:number().default(30).required(),name:string().default('pat').required(),color:string().default('red').required(),});letnameAndAge=person.pick(['name','age']);nameAndAge.getDefault();// => { age: 30, name: 'pat'}
Create a new schema with fields omitted.
letperson=object({age:number().default(30).required(),name:string().default('pat').required(),color:string().default('red').required(),});letnameAndAge=person.omit(['color']);nameAndAge.getDefault();// => { age: 30, name: 'pat'}
Transforms the specified key to a new key. Ifalias
istrue
then the old key will be left.
letschema=object({myProp:mixed(),Other:mixed(),}).from('prop','myProp').from('other','Other',true);schema.cast({prop:5,other:6});// => { myProp: 5, other: 6, Other: 6 }
Validates that the object does not contain extra or unknown properties
The same asobject().validate(value, { stripUnknown: true})
, but as a transform method. When setany unknown properties will be removed.
Validate that the object value only contains keys specified inshape
, passfalse
as the firstargument to disable the check. Restricting keys to known, also enablesstripUnknown
option, when not in strict mode.
Watch Out!: this method performs a transform and a validation, which may produce unexpected results.For more explicit behavior use
object().stripUnknown
andobject().exact()
Transforms all object keys to camelCase
Transforms all object keys to CONSTANT_CASE.
About
Dead simple Object schema validation