Gathering detailed insights and metrics for yup
Gathering detailed insights and metrics for yup
Gathering detailed insights and metrics for yup
Gathering detailed insights and metrics for yup
npm install yup
98.8
Supply Chain
100
Quality
77.8
Maintenance
100
Vulnerability
100
License
v1 Because I finally got around to it
Published on 08 Feb 2023
v1.0.0-beta.7
Published on 22 Aug 2022
v1.0.0-beta.5 - partial fixes and cast migration path
Published on 22 Aug 2022
v1.0.0-beta.4
Published on 22 Aug 2022
Fix object.partial(), `required` changes
Published on 09 Mar 2022
Tuple types
Published on 21 Jan 2022
Module System
Min. Node Version
Typescript Support
Node Version
NPM Version
22,939 Stars
784 Commits
935 Forks
75 Watching
24 Branches
166 Contributors
Updated on 27 Nov 2024
Minified
Minified + Gzipped
TypeScript (96.82%)
JavaScript (3.18%)
Cumulative downloads
Total Downloads
Last day
6.8%
1,578,164
Compared to previous day
Last week
4.7%
7,940,092
Compared to previous week
Last month
12.1%
32,311,737
Compared to previous month
Last year
33.6%
302,660,749
Compared to previous year
4
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:
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.
1import { object, string, number, date, InferType } from 'yup'; 2 3let userSchema = object({ 4 name: string().required(), 5 age: number().required().positive().integer(), 6 email: string().email(), 7 website: string().url().nullable(), 8 createdOn: date().default(() => new Date()), 9}); 10 11// parse and assert validity 12let user = await userSchema.validate(await fetchUser()); 13 14type User = InferType<typeof userSchema>; 15/* { 16 name: string; 17 age: number; 18 email?: string | undefined 19 website?: string | null | undefined 20 createdOn: Date 21}*/
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.
1// Attempts to coerce values to the correct type 2let parsedUser = userSchema.cast({ 3 name: 'jimmy', 4 age: '24', 5 createdOn: '2014-09-23T19:25:25Z', 6}); 7// ✅ { 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.
1// ❌ ValidationError "age is not a number" 2let parsedUser = await userSchema.validate( 3 { 4 name: 'jimmy', 5 age: '24', 6 }, 7 { strict: true }, 8);
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
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.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
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
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.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.
1let num = number().cast('1'); // 1 2 3let obj = object({ 4 firstName: string().lowercase().trim(), 5}) 6 .json() 7 .camelCase() 8 .cast('{"first_name": "jAnE "}'); // { firstName: 'jane' }
Custom transforms can be added
1let reversedString = string() 2 .transform((currentValue) => currentValue.split('').reverse().join('')) 3 .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.
1string() 2 .min(3, 'must be at least 3 characters long') 3 .email('must be a valid email') 4 .validate('no'); // ValidationError
As with transforms, tests can be customized on the fly
1let jamesSchema = string().test( 2 'is-james', 3 (d) => `${d.path} is not James`, 4 (value) => value == null || value === 'James', 5); 6 7jamesSchema.validateSync('James'); // "James" 8 9jamesSchema.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 schema in those cases, you may want to returntrue
for absent values unless your transform makes presence related assertions. The test optionskipAbsent
will 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.
1let order = object({ 2 no: number().required(), 3 sku: string().test({ 4 name: 'is-sku', 5 skipAbsent: true, 6 test(value, ctx) { 7 if (!value.startsWith('s-')) { 8 return ctx.createError({ message: 'SKU missing correct prefix' }) 9 } 10 if (!value.endsWith('-42a')) { 11 return ctx.createError({ message: 'SKU missing correct suffix' }) 12 } 13 if (value.length < 10) { 14 return ctx.createError({ message: 'SKU is not the right length' }) 15 } 16 return true 17 } 18 }) 19}) 20 21order.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.
1let optionalString = string().optional(); 2 3let definedString = optionalString.defined(); 4 5let value = undefined; 6optionalString.isValid(value); // true 7definedString.isValid(value); // false
Yup schema produce static TypeScript interfaces. Use InferType
to extract that interface:
1import * as yup from 'yup'; 2 3let personSchema = yup.object({ 4 firstName: yup.string().defined(), 5 nickName: yup.string().default('').nullable(), 6 sex: yup 7 .mixed() 8 .oneOf(['male', 'female', 'other'] as const) 9 .defined(), 10 email: yup.string().nullable().email(), 11 birthDate: yup.date().nullable().min(new Date(1900, 0, 1)), 12}); 13 14interface Person extends yup.InferType<typeof personSchema> { 15 // using interface instead of type generally gives nicer editor feedback 16}
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()".
1import { string } from 'yup'; 2 3let value: string = string().default('hi').validate(undefined); 4 5// vs 6 7let 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:
1import { object, number, string, ObjectSchema } from 'yup'; 2 3interface Person { 4 name: string; 5 age?: number; 6 sex: 'male' | 'female' | 'other' | null; 7} 8 9// will raise a compile-time type error if the schema does not produce a valid Person 10let schema: ObjectSchema<Person> = object({ 11 name: string().defined(), 12 age: number().optional(), 13 sex: string<'male' | 'female' | 'other'>().nullable().defined(), 14}); 15 16// ❌ errors: 17// "Type 'number | undefined' is not assignable to type 'string'." 18let badSchema: ObjectSchema<Person> = object({ 19 name: number(), 20}); 21
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 ensure you are defining it correctly
1// globals.d.ts 2declare module 'yup' { 3 interface StringSchema<TType, TContext, TDefault, TFlags> { 4 append(appendStr: string): this; 5 } 6} 7 8// app.ts 9import { addMethod, string } from 'yup'; 10 11addMethod(string, 'append', function append(appendStr: string) { 12 return this.transform((value) => `${value}${appendStr}`); 13}); 14 15string().append('~~~~').cast('hi'); // 'hi~~~~'
You must have the strictNullChecks
compiler option enabled for type inference to work.
We also recommend settings strictFunctionTypes
to false
, for functionally better types. Yes
this reduces overall soundness, however TypeScript already disables this check
for methods and constructors (note from TS docs):
During development of this feature, we discovered a large number of inherently unsafe 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 of real 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.
1import { setLocale } from 'yup'; 2 3setLocale({ 4 mixed: { 5 default: 'Não é válido', 6 }, 7 number: { 8 min: 'Deve ser maior que ${min}', 9 }, 10}); 11 12// now use Yup schemas AFTER you defined your custom dictionary 13let schema = yup.object().shape({ 14 name: yup.string(), 15 age: yup.number().min(18), 16}); 17 18try { 19 await schema.validate({ name: 'jimmy', age: 11 }); 20} catch (err) { 21 err.name; // => 'ValidationError' 22 err.errors; // => ['Deve ser maior que 18'] 23}
If you need multi-language support, yup has got you covered. The function setLocale
accepts functions that can be used to
generate error objects with translation keys and values. These can be fed it into your favorite i18n library.
1import { setLocale } from 'yup'; 2 3setLocale({ 4 // use constant translation keys for messages without values 5 mixed: { 6 default: 'field_invalid', 7 }, 8 // use functions to generate an error object that includes the value from the schema 9 number: { 10 min: ({ min }) => ({ key: 'field_too_short', values: { min } }), 11 max: ({ max }) => ({ key: 'field_too_big', values: { max } }), 12 }, 13}); 14 15// ... 16 17let schema = yup.object().shape({ 18 name: yup.string(), 19 age: yup.number().min(18), 20}); 21 22try { 23 await schema.validate({ name: 'jimmy', age: 11 }); 24} catch (err) { 25 messages = err.errors.map((err) => i18next.t(err.key)); 26}
yup
The module export.
1// core schema 2import { 3 mixed, 4 string, 5 number, 6 boolean, 7 bool, 8 date, 9 object, 10 array, 11 ref, 12 lazy, 13} from 'yup'; 14 15// Classes 16import { 17 Schema, 18 MixedSchema, 19 StringSchema, 20 NumberSchema, 21 BooleanSchema, 22 DateSchema, 23 ArraySchema, 24 ObjectSchema, 25} from 'yup'; 26 27// Types 28import type { InferType, ISchema, AnySchema, AnyObjectSchema } from 'yup';
reach(schema: Schema, path: string, value?: object, context?: object): Schema
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 a value
and optionally
a context
object.
1import { reach } from 'yup'; 2 3let schema = object({ 4 nested: object({ 5 arr: array(object({ num: number().max(4) })), 6 }), 7}); 8 9reach(schema, 'nested.arr.num'); 10reach(schema, 'nested.arr[].num'); 11reach(schema, 'nested.arr[1].num'); 12reach(schema, 'nested["arr"][1].num');
addMethod(schemaType: Schema, name: string, method: ()=> Schema): void
Adds a new method to the core schema types. A friendlier convenience method for schemaType.prototype[name] = method
.
1import { addMethod, date } from 'yup';
2
3addMethod(date, 'format', function format(formats, parseStrict) {
4 return this.transform((value, originalValue, ctx) => {
5 if (ctx.isType(value)) return value;
6
7 value = Moment(originalValue, formats, parseStrict);
8
9 return value.isValid() ? value.toDate() : new Date('');
10 });
11});
If you want to add a method to ALL schema types, extend the abstract base class: Schema
1import { addMethod, Schema } from 'yup'; 2 3addMethod(Schema, 'myMethod', ...)
ref(path: string, options: { contextPrefix: string }): Ref
Creates a reference to another sibling or sibling descendant field. Refs are resolved at validation/cast time and supported where specified. Refs are evaluated in the proper order so that the ref value is resolved before the field using the ref (be careful of circular dependencies!).
1import { ref, object, string } from 'yup'; 2 3let schema = object({ 4 baz: ref('foo.bar'), 5 foo: object({ 6 bar: string(), 7 }), 8 x: ref('$x'), 9}); 10 11schema.cast({ foo: { bar: 'boom' } }, { context: { x: 5 } }); 12// => { baz: 'boom', x: 5, foo: { bar: 'boom' } }
lazy((value: any) => Schema): Lazy
Creates a schema that is evaluated at validation/cast time. Useful for creating recursive schema like Trees, for polymorphic fields and arrays.
CAUTION! When defining parent-child recursive object schema, you want to reset the default()
to null
on the child—otherwise the object will infinitely nest itself when you cast it!
1let node = object({ 2 id: number(), 3 child: yup.lazy(() => node.default(undefined)), 4}); 5 6let renderable = yup.lazy((value) => { 7 switch (typeof value) { 8 case 'number': 9 return number(); 10 case 'string': 11 return string(); 12 default: 13 return mixed(); 14 } 15}); 16 17let renderables = array().of(renderable);
ValidationError(errors: string | Array<string>, value: any, path: string)
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 of ValidationErrors
throw earlier in the
validation chain. When the abortEarly
option is false
this is where you can inspect each error thrown,
alternatively, errors
will have all of the messages from each inner error.Schema
Schema
is the abstract base class that all schema type inherit from. It provides a number of base methods and properties
to 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()
Schema.clone(): Schema
Creates a deep copy of the schema. Clone is used internally to return a new schema with every schema state change.
Schema.label(label: string): Schema
Overrides the key name which is used in error messages.
Schema.meta(metadata: SchemaMetadata): Schema
Adds to a metadata object, useful for storing data with a schema, that doesn't belong to the cast object itself.
A custom SchemaMetadata
interface can be defined through
merging
with the CustomSchemaMetadata
interface. Start by creating a yup.d.ts
file
in your package and creating your desired CustomSchemaMetadata
interface:
1// yup.d.ts 2import 'yup'; 3 4declare module 'yup' { 5 // Define your desired `SchemaMetadata` interface by merging the 6 // `CustomSchemaMetadata` interface. 7 export interface CustomSchemaMetadata { 8 placeholderText?: string; 9 tooltipText?: string; 10 // … 11 } 12}
Schema.describe(options?: ResolveOptions): SchemaDescription
Collects schema details (like meta, labels, and active tests) into a serializable description object.
1let schema = object({ 2 name: string().required(), 3}); 4 5let description = schema.describe();
For schema with dynamic components (references, lazy, or conditions), describe requires
more context to accurately return the schema description. In these cases provide options
1import { ref, object, string, boolean } from 'yup'; 2 3let schema = object({ 4 isBig: boolean(), 5 count: number().when('isBig', { 6 is: true, 7 then: (schema) => schema.min(5), 8 otherwise: (schema) => schema.min(0), 9 }), 10}); 11 12schema.describe({ value: { isBig: true } });
And below are the description types, which differ a bit depending on the schema type.
1interface SchemaDescription { 2 type: string; 3 label?: string; 4 meta: object | undefined; 5 oneOf: unknown[]; 6 notOneOf: unknown[]; 7 default?: unknown; 8 nullable: boolean; 9 optional: boolean; 10 tests: Array<{ name?: string; params: ExtraParams | undefined }>; 11 12 // Present on object schema descriptions 13 fields: Record<string, SchemaFieldDescription>; 14 15 // Present on array schema descriptions 16 innerType?: SchemaFieldDescription; 17} 18 19type SchemaFieldDescription = 20 | SchemaDescription 21 | SchemaRefDescription 22 | SchemaLazyDescription; 23 24interface SchemaRefDescription { 25 type: 'ref'; 26 key: string; 27} 28 29interface SchemaLazyDescription { 30 type: string; 31 label?: string; 32 meta: object | undefined; 33}
Schema.concat(schema: Schema): Schema
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 the
base, including type, presence and nullability.
1mixed<string>().defined().concat(mixed<number>().nullable()); 2 3// produces the equivalent to: 4 5mixed<number>().defined().nullable();
Schema.validate(value: any, options?: object): Promise<InferType<Schema>, ValidationError>
Returns the parses and validates an input value, returning the parsed value or throwing an error. This method is asynchronous and returns a Promise object, that is fulfilled with the value, or rejected
with a ValidationError
.
1value = await schema.validate({ name: 'jimmy', age: 24 });
Provide options
to more specifically control the behavior of validate
.
1interface Options { 2 // when true, parsing is skipped and the input is validated "as-is" 3 strict: boolean = false; 4 // Throw on the first error or collect and return all 5 abortEarly: boolean = true; 6 // Remove unspecified keys from objects 7 stripUnknown: boolean = false; 8 // when `false` validations will be performed shallowly 9 recursive: boolean = true; 10 // External values that can be provided to validations and conditionals 11 context?: object; 12}
Schema.validateSync(value: any, options?: object): InferType<Schema>
Runs validatations synchronously if possible and returns the resulting value,
or throws a ValidationError. Accepts all the same options as validate
.
Synchronous validation only works if there are no configured async tests, e.g tests that return a Promise. For instance this will work:
1let schema = number().test( 2 'is-42', 3 "this isn't the number i want", 4 (value) => value != 42, 5); 6 7schema.validateSync(23); // throws ValidationError
however this will not:
1let schema = number().test('is-42', "this isn't the number i want", (value) => 2 Promise.resolve(value != 42), 3); 4 5schema.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 how reach
works,
but uses the resulting schema as the subject for validation.
Note! The
value
here is the root value relative to the starting schema, not the value at the nested path.
1let schema = object({ 2 foo: array().of( 3 object({ 4 loose: boolean(), 5 bar: string().when('loose', { 6 is: true, 7 otherwise: (schema) => schema.strict(), 8 }), 9 }), 10 ), 11}); 12 13let rootValue = { 14 foo: [{ bar: 1 }, { bar: 1, loose: true }], 15}; 16 17await schema.validateAt('foo[0].bar', rootValue); // => ValidationError: must be a string 18 19await schema.validateAt('foo[1].bar', rootValue); // => '1'
Schema.validateSyncAt(path: string, value: any, options?: object): InferType<Schema>
Same as validateAt
but synchronous.
Schema.isValid(value: any, options?: object): Promise<boolean>
Returns true
when the passed in value matches the schema. isValid
is asynchronous and returns a Promise object.
Takes the same options as validate()
.
Schema.isValidSync(value: any, options?: object): boolean
Synchronously returns true
when the passed in value matches the schema.
Takes the same options as validateSync()
and has the same caveats around async tests.
Schema.cast(value: any, options = {}): InferType<Schema>
Attempts to coerce the passed in value to a value that matches the schema. For example: '5'
will
cast to 5
when using the number()
type. Failed casts generally return null
, but may also
return results like NaN
and unexpected strings.
Provide options
to more specifically control the behavior of validate
.
1interface CastOptions<TContext extends {}> { 2 // Remove undefined properties from objects 3 stripUnknown: boolean = false; 4 5 // Throws a TypeError if casting doesn't produce a valid type 6 // note that the TS return type is inaccurate when this is `false`, use with caution 7 assert?: boolean = true; 8 9 // External values that used to resolve conditions and references 10 context?: TContext; 11}
Schema.isType(value: any): value is InferType<Schema>
Runs a type check against the passed in value
. It returns true if it matches,
it does not cast the value. When nullable()
is set null
is considered a valid value of the type.
You should use isType
for all Schema type checks.
Schema.strict(enabled: boolean = false): Schema
Sets the strict
option to true
. Strict schemas skip coercion and transformation attempts,
validating the value "as is".
Schema.strip(enabled: boolean = true): Schema
Marks a schema to be removed from an output object. Only works as a nested schema.
1let schema = object({ 2 useThis: number(), 3 notThis: string().strip(), 4}); 5 6schema.cast({ notThis: 'foo', useThis: 4 }); // => { useThis: 4 }
Schema with strip
enabled have an inferred type of never
, allowing them to be
removed from the overall type:
1let schema = object({ 2 useThis: number(), 3 notThis: string().strip(), 4}); 5 6InferType<typeof schema>; /* 7{ 8 useThis?: number | undefined 9} 10*/
Schema.withMutation(builder: (current: Schema) => void): void
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 initial
declaration, and only happen once over the lifetime of the schema, so performance isn't an issue.
However certain mutations do occur at cast/validation time, (such as conditional schema using when()
), or
when instantiating a schema object.
1object() 2 .shape({ key: string() }) 3 .withMutation((schema) => { 4 return arrayOfObjectTests.forEach((test) => { 5 schema.test(test); 6 }); 7 });
Schema.default(value: any): Schema
Sets a default value to use when the value is undefined
.
Defaults are created after transformations are executed, but before validations, to help ensure that safe
defaults are specified. The default value will be cloned on each use, which can incur performance penalty
for objects and arrays. To avoid this overhead you can also pass a function that returns a new default.
Note that null
is considered a separate non-empty value.
1yup.string.default('nothing'); 2 3yup.object.default({ number: 5 }); // object will be cloned every time a default is needed 4 5yup.object.default(() => ({ number: 5 })); // this is cheaper 6 7yup.date.default(() => new Date()); // also helpful for defaults that change over time
Schema.getDefault(options?: object): Any
Retrieve a previously set default value. getDefault
will resolve any conditions that may alter the default. Optionally pass options
with context
(for more info on context
see Schema.validate
).
Schema.nullable(message?: string | function): Schema
Indicates that null
is a valid value for the schema. Without nullable()
null
is treated as a different type and will fail Schema.isType()
checks.
1let schema = number().nullable(); 2 3schema.cast(null); // null 4 5InferType<typeof schema>; // number | null
Schema.nonNullable(message?: string | function): Schema
The opposite of nullable
, removes null
from valid type values for the schema.
Schema are non nullable by default.
1let schema = number().nonNullable(); 2 3schema.cast(null); // TypeError 4 5InferType<typeof schema>; // number
Schema.defined(): Schema
Require a value for the schema. All field values apart from undefined
meet this requirement.
1let schema = string().defined(); 2 3schema.cast(undefined); // TypeError 4 5InferType<typeof schema>; // string
Schema.optional(): Schema
The opposite of defined()
allows undefined
values for the given type.
1let schema = string().optional(); 2 3schema.cast(undefined); // undefined 4 5InferType<typeof schema>; // string | undefined
Schema.required(message?: string | function): Schema
Mark the schema as required, which will not allow undefined
or null
as a value. required
negates the effects of calling optional()
and nullable()
Watch out!
string().required
) works a little different and additionally prevents empty string values (''
) when required.
Schema.notRequired(): Schema
Mark the schema as not required. This is a shortcut for schema.nullable().optional()
;
Schema.typeError(message: string): Schema
Define an error message for failed type checks. The ${value}
and ${type}
interpolation can
be used in the message
argument.
Schema.oneOf(arrayOfValues: Array<any>, message?: string | function): Schema
Alias: equals
Only allow values from set of values. Values added are removed from any notOneOf
values if present.
The ${values}
interpolation can be used in the message
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 checked
at validation time.
Note that undefined
does not fail this validator, even when undefined
is not included in arrayOfValues
.
If you don't want undefined
to be a valid value, you can use Schema.required
.
1let schema = yup.mixed().oneOf(['jimmy', 42]); 2 3await schema.isValid(42); // => true 4await schema.isValid('jimmy'); // => true 5await schema.isValid(new Date()); // => false
Schema.notOneOf(arrayOfValues: Array<any>, message?: string | function)
Disallow values from a set of values. Values added are removed from oneOf
values if present.
The ${values}
interpolation can be used in the message
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 checked
at validation time.
1let schema = yup.mixed().notOneOf(['jimmy', 42]); 2 3await schema.isValid(42); // => false 4await schema.isValid(new Date()); // => true
Schema.when(keys: string | string[], builder: object | (values: any[], schema) => Schema): Schema
Adjust the schema based on a sibling or sibling children fields. You can provide an object
literal where the key is
is value or a matcher function, then
provides the true schema and/or
otherwise
for the failure condition.
is
conditions are strictly compared (===
) if you want to use a different form of equality you
can provide a function like: is: (value) => value == true
.
You can also prefix properties with $
to specify a property that is dependent
on context
passed in by validate()
or cast
instead of the input value.
when
conditions are additive.
then
and otherwise
are specified functions (schema: Schema) => Schema
.
1let schema = object({ 2 isBig: boolean(), 3 count: number() 4 .when('isBig', { 5 is: true, // alternatively: (val) => val == true 6 then: (schema) => schema.min(5), 7 otherwise: (schema) => schema.min(0), 8 }) 9 .when('$other', ([other], schema) => 10 other === 4 ? schema.max(6) : schema, 11 ), 12}); 13 14await schema.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.
1let schema = object({ 2 isSpecial: boolean(), 3 isBig: boolean(), 4 count: number().when(['isBig', 'isSpecial'], { 5 is: true, // alternatively: (isBig, isSpecial) => isBig && isSpecial 6 then: (schema) => schema.min(5), 7 otherwise: (schema) => schema.min(0), 8 }), 9}); 10 11await schema.validate({ 12 isBig: true, 13 isSpecial: true, 14 count: 10, 15});
Alternatively you can provide a function that returns a schema, called with an array of values for each provided key the current schema.
1let schema = yup.object({ 2 isBig: yup.boolean(), 3 count: yup.number().when('isBig', ([isBig], schema) => { 4 return isBig ? schema.min(5) : schema.min(0); 5 }), 6}); 7 8await schema.validate({ isBig: false, count: 4 });
Schema.test(name: string, message: string | function | any, test: function): Schema
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 validations all (or no) tests are run asynchronously. A consequence of this is that test execution order cannot be guaranteed.
All tests must provide a name
, an error message
and a validation function that must return
true
when the current value
is valid and false
or a ValidationError
otherwise.
To make a test async return a promise that resolves true
or false
or a ValidationError
.
For the message
argument you can provide a string which will interpolate certain values
if specified using the ${param}
syntax. By default all test messages are passed a path
value
which is valuable in nested schemas.
The test
function is called with the current value
. For more advanced validations you can
use the alternate signature to provide more options (see below):
1let jimmySchema = string().test( 2 'is-jimmy', 3 '${path} is not Jimmy', 4 (value, context) => value === 'jimmy', 5); 6 7// or make it async by returning a promise 8let asyncJimmySchema = string() 9 .label('First name') 10 .test( 11 'is-jimmy', 12 ({ label }) => `${label} is not Jimmy`, // a message can also be a function 13 async (value, testContext) => 14 (await fetch('/is-jimmy/' + value)).responseText === 'true', 15 ); 16 17await schema.isValid('jimmy'); // => true 18await schema.isValid('john'); // => false
Test functions are called with a special context value, as the second argument, that exposes some useful metadata
and functions. For non arrow functions, the test context is also set as the function this
. Watch out, if you access
it via this
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
: the options
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 a
validation error. Useful for dynamically setting the path
, params
, or more likely, the error message
.
If either option is omitted it will use the current path, or default message.Schema.test(options: object): Schema
Alternative test(..)
signature. options
is an object containing some of the following options:
1Options = { 2 // unique name identifying the test 3 name: string; 4 // test function, determines schema validity 5 test: (value: any) => boolean; 6 // the validation error message 7 message: string; 8 // values passed to message for interpolation 9 params: ?object; 10 // mark the test as exclusive, meaning only one test of the same name can be active at once 11 exclusive: boolean = false; 12}
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 name the 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 name the previous tests are removed and further tests of the same name will replace each other.
1let max = 64; 2let schema = yup.string().test({ 3 name: 'max', 4 exclusive: true, 5 params: { max }, 6 message: '${path} must be less than ${max} characters', 7 test: (value) => value == null || value.length <= max, 8});
Schema.transform((currentValue: any, originalValue: any) => any): Schema
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 by isType()
). transforms are run before validations and only applied when the schema is not marked as strict
(the default). Some types have built in transformations.
Transformations are useful for arbitrarily altering how the object is cast, however, you should take care
not to mutate the passed in value. Transforms are run sequentially so each value
represents the
current state of the cast, you can use the originalValue
param if you need to work on the raw initial value.
1let schema = string().transform((value, originalValue) => { 2 return this.isType(value) && value !== null ? value.toUpperCase() : value; 3}); 4 5schema.cast('jimmy'); // => 'JIMMY'
Each types will handle basic coercion of values to the proper type for you, but occasionally you may want to adjust or refine the default behavior. For example, if you wanted to use a different date parsing strategy than the default one you could do that with a transform.
1module.exports = function (formats = 'MMM dd, yyyy') { 2 return date().transform((value, originalValue, context) => { 3 // check to see if the previous transform already parsed the date 4 if (context.isType(value)) return value; 5 6 // the default coercion failed so let's try it with Moment.js instead 7 value = Moment(originalValue, formats); 8 9 // if it's valid return the date object, otherwise return an `InvalidDate` 10 return value.isValid() ? value.toDate() : new Date(''); 11 }); 12};
Creates a schema that matches all types, or just the ones you configure. Inherits from Schema
.
Mixed types extends {}
by default instead of any
or unknown
. This is because in TypeScript {}
means
anything that isn't null
or undefined
which yup treats distinctly.
1import { mixed, InferType } from 'yup'; 2 3let schema = mixed().nullable(); 4 5schema.validateSync('string'); // 'string'; 6 7schema.validateSync(1); // 1; 8 9schema.validateSync(new Date()); // Date; 10 11InferType<typeof schema>; // {} | undefined 12 13InferType<typeof schema.nullable().defined()>; // {} | null
Custom types can be implemented by passing a type check
function. This will also
narrow the TypeScript type for the schema.
1import { mixed, InferType } from 'yup'; 2 3let objectIdSchema = yup 4 .mixed((input): input is ObjectId => input instanceof ObjectId) 5 .transform((value: any, input, ctx) => { 6 if (ctx.isType(value)) return value; 7 return new ObjectId(value); 8 }); 9 10await objectIdSchema.validate(ObjectId('507f1f77bcf86cd799439011')); // ObjectId("507f1f77bcf86cd799439011") 11 12await objectIdSchema.validate('507f1f77bcf86cd799439011'); // ObjectId("507f1f77bcf86cd799439011") 13 14InferType<typeof objectIdSchema>; // ObjectId
Define a string schema. Inherits from Schema
.
1let schema = yup.string(); 2 3await schema.isValid('hello'); // => true
By default, the cast
logic of string
is to call toString
on the value if it exists.
empty values are not coerced (use ensure()
to coerce empty values to empty strings).
Failed casts return the input value.
string.required(message?: string | function): Schema
The same as the mixed()
schema required, except that empty strings are also considered 'missing' values.
string.length(limit: number | Ref, message?: string | function): Schema
Set a required length for the string value. The ${length}
interpolation can be used in the message
argument
string.min(limit: number | Ref, message?: string | function): Schema
Set a minimum length limit for the string value. The ${min}
interpolation can be used in the message
argument
string.max(limit: number | Ref, message?: string | function): Schema
Set a maximum length limit for the string value. The ${max}
interpolation can be used in the message
argument
string.matches(regex: Regex, message?: string | function): Schema
Provide an arbitrary regex
to match the value against.
1let schema = string().matches(/(hi|bye)/); 2 3await schema.isValid('hi'); // => true 4await schema.isValid('nope'); // => false
string.matches(regex: Regex, options: { message: string, excludeEmptyString: bool }): Schema
An alternate signature for string.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 avoid
matching nothing without complicating the regex.
1let schema = string().matches(/(hi|bye)/, { excludeEmptyString: true }); 2 3await schema.isValid(''); // => true
string.email(message?: string | function): Schema
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. Different clients 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 email to it and check that the user got it. With that in mind, yup picks a relatively simple regex that does not cover all cases, but aligns with browser input validation behavior since HTML forms are a common use case for yup.
If you have more specific needs please override the email method with your own logic or regex:
1yup.addMethod(yup.string, 'email', function validateEmail(message) { 2 return this.matches(myEmailRegex, { 3 message, 4 name: 'email', 5 excludeEmptyString: true, 6 }); 7});
string.url(message?: string | function): Schema
Validates the value as a valid URL via a regex.
string.uuid(message?: string | function): Schema
Validates the value as a valid UUID via a regex.
string.datetime(options?: {message?: string | function, allowOffset?: boolean, precision?: number})
Validates the value as an ISO datetime via a regex. Defaults to UTC validation; timezone offsets are not permitted (see options.allowOffset
).
Unlike .date()
, datetime
will not convert the string to a Date
object. datetime
also provides greater customization over the required format of the datetime string than date
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)
string.datetime(message?: string | function)
An alternate signature for string.datetime
that can be used when you don't need to pass options other than message
.
string.ensure(): Schema
Transforms undefined
and null
values to an empty string along with
setting the default
to an empty string.
string.trim(message?: string | function): Schema
Transforms string values by removing leading and trailing whitespace. If
strict()
is set it will only validate that the value is trimmed.
string.lowercase(message?: string | function): Schema
Transforms the string value to lowercase. If strict()
is set it
will only validate that the value is lowercase.
string.uppercase(message?: string | function): Schema
Transforms the string value to uppercase. If strict()
is set it
will only validate that the value is uppercase.
Define a number schema. Inherits from Schema
.
1let schema = yup.number(); 2 3await schema.isValid(10); // => true
The default cast
logic of number
is: parseFloat
.
Failed casts return NaN
.
number.min(limit: number | Ref, message?: string | function): Schema
Set the minimum value allowed. The ${min}
interpolation can be used in the
message
argument.
number.max(limit: number | Ref, message?: string | function): Schema
Set the maximum value allowed. The ${max}
interpolation can be used in the
message
argument.
number.lessThan(max: number | Ref, message?: string | function): Schema
Value must be less than max
. The ${less}
interpolation can be used in the
message
argument.
number.moreThan(min: number | Ref, message?: string | function): Schema
Value must be strictly greater than min
. The ${more}
interpolation can be used in the
message
argument.
number.positive(message?: string | function): Schema
Value must be a positive number.
number.negative(message?: string | function): Schema
Value must be a negative number.
number.integer(message?: string | function): Schema
Validates that a number is an integer.
number.truncate(): Schema
Transformation that coerces the value to an integer by stripping off the digits to the right of the decimal point.
number.round(type: 'floor' | 'ceil' | 'trunc' | 'round' = 'round'): Schema
Adjusts the value via the specified method of Math
(defaults to 'round').
Define a boolean schema. Inherits from Schema
.
1let schema = yup.boolean(); 2 3await schema.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 from Schema
.
1let schema = yup.date(); 2 3await schema.isValid(new Date()); // => true
The default cast
logic of date
is pass the value to the Date
constructor, failing that, it will attempt
to 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.
date.min(limit: Date | string | Ref, message?: string | function): Schema
Set the minimum date allowed. When a string is provided it will attempt to cast to a date first and use the result as the limit.
date.max(limit: Date | string | Ref, message?: string | function): Schema
Set the maximum date allowed, When a string is provided it will attempt to cast to a date first and use the result as the limit.
Define an array schema. Arrays can be typed or not, When specifying the element type, cast
and isValid
will apply to the elements as well. Options passed into isValid
are also passed to child schemas.
Inherits from Schema
.
1let schema = yup.array().of(yup.number().min(2)); 2 3await schema.isValid([2, 3]); // => true 4await schema.isValid([1, -24]); // => false 5 6schema.cast(['2', '3']); // => [2, 3]
You can also pass a subtype schema to the array constructor as a convenience.
1array().of(yup.number()); 2// or 3array(yup.number());
Arrays have no default casting behavior.
array.of(type: Schema): this
Specify the schema of array elements. of()
is optional and when omitted the array schema will
not validate its contents.
array.json(): this
Attempt to parse input string values as JSON using JSON.parse
.
array.length(length: number | Ref, message?: string | function): this
Set a specific length requirement for the array. The ${length}
interpolation can be used in the message
argument.
array.min(limit: number | Ref, message?: string | function): this
Set a minimum length limit for the array. The ${min}
interpolation can be used in the message
argument.
array.max(limit: number | Ref, message?: string | function): this
Set a maximum length limit for the array. The ${max}
interpolation can be used in the message
argument.
array.ensure(): this
Ensures that the value is an array, by setting the default to []
and transforming null
and undefined
values to an empty array as well. Any non-empty, non-array value will be wrapped in an array.
1array().ensure().cast(null); // => [] 2array().ensure().cast(1); // => [1] 3array().ensure().cast([1]); // => [1]
array.compact(rejector: (value) => boolean): Schema
Removes falsey values from the array. Providing a rejecter function lets you specify the rejection criteria yourself.
1array().compact().cast(['', 1, 0, 4, false, null]); // => [1, 4] 2 3array() 4 .compact(function (v) { 5 return v == null; 6 }) 7 .cast(['', 1, 0, 4, false, null]); // => ['', 1, 0, 4, false]
Tuples, are fixed length arrays where each item has a distinct type.
Inherits from Schema
.
1import { tuple, string, number, InferType } from 'yup'; 2 3let schema = tuple([ 4 string().label('name'), 5 number().label('age').positive().integer(), 6]); 7 8await schema.validate(['James', 3]); // ['James', 3] 9 10await schema.validate(['James', -24]); // => ValidationError: age must be a positive number 11 12InferType<typeof schema> // [string, number] | undefined
tuples have no default casting behavior.
Define an object schema. Options passed into isValid
are also passed to child schemas.
Inherits from Schema
.
1yup.object({ 2 name: string().required(), 3 age: number().required().positive().integer(), 4 email: string().email(), 5 website: string().url(), 6});
object schema do not have any default transforms applied.
Object schema come with a default value already set, which "builds" out the object shape, a sets any defaults for fields:
1let schema = object({ 2 name: string().default(''), 3}); 4 5schema.default(); // -> { name: '' }
This may be a bit surprising, but is usually helpful since it allows large, nested schema to create default values that fill out the whole shape and not just the root object. There is one gotcha! though. For nested object schema that are optional but include non optional fields may fail in unexpected ways:
1let schema = object({ 2 id: string().required(), 3 names: object({ 4 first: string().required(), 5 }), 6}); 7 8schema.isValid({ id: 1 }); // false! names.first is required
This is because yup casts the input object before running validation which will produce:
{ id: '1', names: { first: undefined }}
During the validation phase names
exists, and is validated, finding names.first
missing.
If you wish to avoid this behavior do one of the following:
names.default(undefined)
names.nullable().default(null)
object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema
Define the keys of the object and the schemas for said keys.
Note that you can chain shape
method, which acts like Object.assign
.
1object({ 2 a: string(), 3 b: number(), 4}).shape({ 5 b: string(), 6 c: number(), 7});
would be exactly the same as:
1object({ 2 a: string(), 3 b: string(), 4 c: number(), 5});
object.json(): this
Attempt to parse input string values as JSON using JSON.parse
.
object.concat(schemaB: ObjectSchema): ObjectSchema
Creates a object schema, by applying all settings and fields from schemaB
to the base, producing a new schema.
The object shape is shallowly merged with common fields from schemaB
taking precedence over the base
fields.
object.pick(keys: string[]): Schema
Create a new schema from a subset of the original's fields.
1let person = object({ 2 age: number().default(30).required(), 3 name: string().default('pat').required(), 4 color: string().default('red').required(), 5}); 6 7let nameAndAge = person.pick(['name', 'age']); 8nameAndAge.getDefault(); // => { age: 30, name: 'pat'}
object.omit(keys: string[]): Schema
Create a new schema with fields omitted.
1let person = object({ 2 age: number().default(30).required(), 3 name: string().default('pat').required(), 4 color: string().default('red').required(), 5}); 6 7let nameAndAge = person.omit(['color']); 8nameAndAge.getDefault(); // => { age: 30, name: 'pat'}
object.from(fromKey: string, toKey: string, alias: boolean = false): this
Transforms the specified key to a new key. If alias
is true
then the old key will be left.
1let schema = object({ 2 myProp: mixed(), 3 Other: mixed(), 4}) 5 .from('prop', 'myProp') 6 .from('other', 'Other', true); 7 8schema.cast({ prop: 5, other: 6 }); // => { myProp: 5, other: 6, Other: 6 }
object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema
Validate that the object value only contains keys specified in shape
, pass false
as the first
argument to disable the check. Restricting keys to known, also enables stripUnknown
option, when not in strict mode.
object.camelCase(): Schema
Transforms all object keys to camelCase
object.constantCase(): Schema
Transforms all object keys to CONSTANT_CASE.
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 16/30 approved changesets -- score normalized to 5
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
0 commit(s) and 1 issue activity found in the last 90 days -- score normalized to 0
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
62 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-11-18
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More