0
# Utilities
1
2
Utility functions and advanced features for schema manipulation, references, lazy evaluation, and customization including localization and custom method extension.
3
4
## Capabilities
5
6
### Schema Navigation
7
8
Functions for navigating and extracting nested schemas from complex object structures.
9
10
```typescript { .api }
11
/**
12
* Navigate to a nested schema at a specific path
13
* @param schema - Root schema to navigate from
14
* @param path - Dot-notation path to target schema
15
* @param value - Optional value for context-dependent schemas
16
* @param context - Optional validation context
17
* @returns Schema at the specified path
18
* @throws Error if path is not found
19
*/
20
function reach(
21
schema: Schema,
22
path: string,
23
value?: any,
24
context?: any
25
): Schema;
26
27
/**
28
* Get nested schema and context information at path
29
* @param schema - Root schema to navigate from
30
* @param path - Dot-notation path to target schema
31
* @param value - Optional value for context-dependent schemas
32
* @param context - Optional validation context
33
* @returns Object containing schema and context at path
34
*/
35
function getIn(
36
schema: Schema,
37
path: string,
38
value?: any,
39
context?: any
40
): {
41
schema: Schema;
42
parent: any;
43
parentPath: string;
44
};
45
```
46
47
**Usage Examples:**
48
49
```typescript
50
import { object, string, array, reach, getIn } from "yup";
51
52
const userSchema = object({
53
profile: object({
54
name: string().required(),
55
addresses: array(object({
56
street: string().required(),
57
city: string().required(),
58
})),
59
}),
60
});
61
62
// Navigate to nested schema
63
const nameSchema = reach(userSchema, "profile.name");
64
const addressSchema = reach(userSchema, "profile.addresses[0]");
65
const citySchema = reach(userSchema, "profile.addresses[0].city");
66
67
// Validate using reached schema
68
await nameSchema.validate("John Doe"); // "John Doe"
69
70
// Get schema with context
71
const result = getIn(userSchema, "profile.addresses[0].city");
72
console.log(result.schema); // StringSchema for city
73
console.log(result.parentPath); // "profile.addresses[0]"
74
```
75
76
### Schema Detection
77
78
Utility for identifying yup schema objects.
79
80
```typescript { .api }
81
/**
82
* Check if a value is a yup schema
83
* @param value - Value to test
84
* @returns Type predicate indicating if value is a Schema
85
*/
86
function isSchema(value: any): value is Schema;
87
```
88
89
**Usage Examples:**
90
91
```typescript
92
import { string, number, isSchema } from "yup";
93
94
const stringSchema = string();
95
const numberValue = 42;
96
const plainObject = { type: "string" };
97
98
console.log(isSchema(stringSchema)); // true
99
console.log(isSchema(numberValue)); // false
100
console.log(isSchema(plainObject)); // false
101
102
// Use in conditional logic
103
function processInput(input: any) {
104
if (isSchema(input)) {
105
return input.describe();
106
} else {
107
return { value: input };
108
}
109
}
110
```
111
112
### References
113
114
Create references to other fields in the same validation context for cross-field validation and dependencies.
115
116
```typescript { .api }
117
/**
118
* Create a reference to another field in the validation context
119
* @param path - Path to the referenced field
120
* @param options - Reference configuration options
121
* @returns Reference object pointing to the specified path
122
*/
123
function ref(path: string, options?: ReferenceOptions): Reference;
124
125
interface ReferenceOptions {
126
/** Transform the referenced value before using it */
127
map?: (value: any) => any;
128
}
129
130
/**
131
* Reference class for cross-field dependencies
132
*/
133
class Reference {
134
/** Path being referenced */
135
key: string;
136
/** Whether reference points to validation context ($) */
137
isContext: boolean;
138
/** Whether reference points to current value (.) */
139
isValue: boolean;
140
/** Whether reference points to sibling field */
141
isSibling: boolean;
142
/** Parsed path segments */
143
path: string[];
144
/** Function to extract property value */
145
getter: (data: any) => any;
146
/** Optional value transformation function */
147
map?: (value: any) => any;
148
149
/**
150
* Get the referenced value from provided data
151
* @param value - Current field value
152
* @param parent - Parent object containing fields
153
* @param context - Validation context
154
* @returns Referenced value
155
*/
156
getValue(value?: any, parent?: any, context?: any): any;
157
158
/**
159
* Cast the referenced value
160
* @param value - Value to cast
161
* @param options - Cast options
162
* @returns Cast value
163
*/
164
cast(value: any, options?: CastOptions): any;
165
166
/**
167
* Resolve the reference (returns self)
168
* @returns This reference instance
169
*/
170
resolve(): Reference;
171
172
/**
173
* Get a description of the reference
174
* @returns Reference description object
175
*/
176
describe(): { type: "ref"; key: string };
177
178
/**
179
* String representation of the reference
180
* @returns String representation
181
*/
182
toString(): string;
183
184
/**
185
* Check if a value is a Reference
186
* @param value - Value to test
187
* @returns Type predicate for Reference
188
*/
189
static isRef(value: any): value is Reference;
190
}
191
```
192
193
**Usage Examples:**
194
195
```typescript
196
import { object, string, number, ref } from "yup";
197
198
// Basic reference usage
199
const schema = object({
200
password: string().min(8).required(),
201
confirmPassword: string()
202
.oneOf([ref("password")], "Passwords must match")
203
.required(),
204
});
205
206
// Reference with transformation
207
const priceSchema = object({
208
price: number().positive().required(),
209
discountedPrice: number()
210
.max(ref("price", {
211
map: (price) => price * 0.9 // 10% max discount
212
}), "Discount cannot exceed 10%")
213
.positive(),
214
});
215
216
// Context references (access validation context)
217
const contextSchema = object({
218
userRole: string().required(),
219
adminAction: string().when("$isAdmin", {
220
is: true,
221
then: (schema) => schema.required(),
222
otherwise: (schema) => schema.strip(),
223
}),
224
});
225
226
await contextSchema.validate(
227
{ userRole: "user", adminAction: "delete" },
228
{ context: { isAdmin: false } }
229
);
230
231
// Complex reference paths
232
const nestedSchema = object({
233
users: array(object({
234
name: string().required(),
235
email: string().email().required(),
236
})),
237
primaryUser: object({
238
name: string().oneOf([ref("users[0].name")], "Must match first user"),
239
}),
240
});
241
```
242
243
### Lazy Schemas
244
245
Create schemas that are resolved dynamically based on the value being validated, enabling recursive and self-referencing schemas.
246
247
```typescript { .api }
248
/**
249
* Create a lazy schema that resolves based on the value
250
* @param builder - Function that returns schema based on value
251
* @returns LazySchema instance
252
*/
253
function lazy<T>(
254
builder: (value: any, options: ResolveOptions) => Schema<T>
255
): LazySchema<T>;
256
257
interface ResolveOptions {
258
/** Current validation context */
259
context?: any;
260
/** Parent object */
261
parent?: any;
262
/** Field path */
263
path?: string;
264
}
265
266
/**
267
* Lazy schema that resolves to different schemas based on value
268
*/
269
class LazySchema<T = any> {
270
/**
271
* Clone the lazy schema
272
* @param spec - Modifications to apply
273
* @returns New LazySchema instance
274
*/
275
clone(spec?: Partial<LazySchemaSpec>): LazySchema<T>;
276
277
/**
278
* Make the lazy schema optional
279
* @returns New optional LazySchema
280
*/
281
optional(): LazySchema<T | undefined>;
282
283
/**
284
* Resolve to concrete schema based on value
285
* @param options - Resolution context options
286
* @returns Resolved concrete schema
287
*/
288
resolve(options: ResolveOptions): Schema<T>;
289
290
/**
291
* Cast value using resolved schema
292
* @param value - Value to cast
293
* @param options - Cast options
294
* @returns Cast value
295
*/
296
cast(value: any, options?: CastOptions): T;
297
298
/**
299
* Validate value using resolved schema
300
* @param value - Value to validate
301
* @param options - Validation options
302
* @returns Promise resolving to validated value
303
*/
304
validate(value: any, options?: ValidateOptions): Promise<T>;
305
306
/**
307
* Synchronously validate value using resolved schema
308
* @param value - Value to validate
309
* @param options - Validation options
310
* @returns Validated value
311
*/
312
validateSync(value: any, options?: ValidateOptions): T;
313
314
/**
315
* Validate at specific path using resolved schema
316
* @param path - Field path
317
* @param value - Root value
318
* @param options - Validation options
319
* @returns Promise resolving to field value
320
*/
321
validateAt(path: string, value: any, options?: ValidateOptions): Promise<any>;
322
323
/**
324
* Synchronously validate at path using resolved schema
325
* @param path - Field path
326
* @param value - Root value
327
* @param options - Validation options
328
* @returns Field value
329
*/
330
validateSyncAt(path: string, value: any, options?: ValidateOptions): any;
331
332
/**
333
* Check validity using resolved schema
334
* @param value - Value to check
335
* @param options - Validation options
336
* @returns Promise resolving to validity status
337
*/
338
isValid(value: any, options?: ValidateOptions): Promise<boolean>;
339
340
/**
341
* Synchronously check validity using resolved schema
342
* @param value - Value to check
343
* @param options - Validation options
344
* @returns Validity status
345
*/
346
isValidSync(value: any, options?: ValidateOptions): boolean;
347
348
/**
349
* Describe the lazy schema or resolved schema
350
* @param options - Description options
351
* @returns Schema description
352
*/
353
describe(options?: DescribeOptions): SchemaDescription;
354
355
/**
356
* Set or get lazy schema metadata
357
* @param metadata - Metadata to set (optional)
358
* @returns Metadata or new schema with metadata
359
*/
360
meta(): Record<string, any> | undefined;
361
meta(metadata: Record<string, any>): LazySchema<T>;
362
}
363
```
364
365
**Usage Examples:**
366
367
```typescript
368
import { object, string, array, lazy } from "yup";
369
370
// Recursive schema for nested comments
371
const commentSchema = lazy(() =>
372
object({
373
id: string().required(),
374
message: string().required(),
375
author: string().required(),
376
replies: array(commentSchema), // Self-reference
377
})
378
);
379
380
// Polymorphic schema based on type field
381
const shapeSchema = lazy((value) => {
382
switch (value?.type) {
383
case "circle":
384
return object({
385
type: string().equals(["circle"]),
386
radius: number().positive().required(),
387
});
388
case "rectangle":
389
return object({
390
type: string().equals(["rectangle"]),
391
width: number().positive().required(),
392
height: number().positive().required(),
393
});
394
default:
395
return object({
396
type: string().required(),
397
});
398
}
399
});
400
401
// Dynamic schema based on user role
402
const userDataSchema = lazy((value, options) => {
403
const isAdmin = options.context?.userRole === "admin";
404
405
const baseSchema = object({
406
name: string().required(),
407
email: string().email().required(),
408
});
409
410
if (isAdmin) {
411
return baseSchema.shape({
412
adminNotes: string(),
413
permissions: array(string()),
414
});
415
}
416
417
return baseSchema;
418
});
419
420
// Usage with context
421
await userDataSchema.validate(
422
{ name: "John", email: "john@example.com" },
423
{ context: { userRole: "admin" } }
424
);
425
```
426
427
### Value Formatting
428
429
Format values for display in error messages and debugging.
430
431
```typescript { .api }
432
/**
433
* Format a value for display in error messages
434
* @param value - Value to format
435
* @returns Human-readable string representation
436
*/
437
function printValue(value: any): string;
438
```
439
440
**Usage Examples:**
441
442
```typescript
443
import { printValue } from "yup";
444
445
console.log(printValue("hello")); // '"hello"'
446
console.log(printValue(42)); // "42"
447
console.log(printValue(null)); // "null"
448
console.log(printValue(undefined)); // "undefined"
449
console.log(printValue([1, 2, 3])); // "[1, 2, 3]"
450
console.log(printValue({ name: "John" })); // '{"name": "John"}'
451
console.log(printValue(new Date("2023-01-01"))); // "2023-01-01T00:00:00.000Z"
452
453
// Used in custom error messages
454
const customSchema = string().test(
455
"custom-test",
456
function(value) {
457
return `Expected string, got ${printValue(value)}`;
458
},
459
(value) => typeof value === "string"
460
);
461
```
462
463
### Localization
464
465
Customize error messages for different languages and locales.
466
467
```typescript { .api }
468
/**
469
* Set custom error messages for validation failures
470
* @param localeObject - Object containing custom error messages
471
*/
472
function setLocale(localeObject: LocaleObject): void;
473
474
/**
475
* Default English error message locale
476
*/
477
const defaultLocale: LocaleObject;
478
479
interface LocaleObject {
480
mixed?: MixedLocale;
481
string?: StringLocale;
482
number?: NumberLocale;
483
date?: DateLocale;
484
boolean?: BooleanLocale;
485
object?: ObjectLocale;
486
array?: ArrayLocale;
487
}
488
489
interface MixedLocale {
490
default?: string;
491
required?: string;
492
oneOf?: string;
493
notOneOf?: string;
494
defined?: string;
495
notType?: string;
496
}
497
498
interface StringLocale extends MixedLocale {
499
length?: string;
500
min?: string;
501
max?: string;
502
matches?: string;
503
email?: string;
504
url?: string;
505
uuid?: string;
506
trim?: string;
507
lowercase?: string;
508
uppercase?: string;
509
}
510
511
interface NumberLocale extends MixedLocale {
512
min?: string;
513
max?: string;
514
lessThan?: string;
515
moreThan?: string;
516
positive?: string;
517
negative?: string;
518
integer?: string;
519
}
520
521
interface DateLocale extends MixedLocale {
522
min?: string;
523
max?: string;
524
}
525
526
interface BooleanLocale extends MixedLocale {
527
isValue?: string;
528
}
529
530
interface ObjectLocale extends MixedLocale {
531
noUnknown?: string;
532
}
533
534
interface ArrayLocale extends MixedLocale {
535
min?: string;
536
max?: string;
537
length?: string;
538
}
539
```
540
541
**Usage Examples:**
542
543
```typescript
544
import { setLocale, string, number } from "yup";
545
546
// Set Spanish error messages
547
setLocale({
548
mixed: {
549
required: "Este campo es obligatorio",
550
notType: "Debe ser de tipo ${type}",
551
},
552
string: {
553
min: "Debe tener al menos ${min} caracteres",
554
max: "Debe tener como máximo ${max} caracteres",
555
email: "Debe ser un email válido",
556
},
557
number: {
558
min: "Debe ser mayor o igual a ${min}",
559
max: "Debe ser menor o igual a ${max}",
560
positive: "Debe ser un número positivo",
561
},
562
});
563
564
// Schemas will now use Spanish messages
565
const userSchema = object({
566
nombre: string().min(2).required(),
567
edad: number().positive().required(),
568
email: string().email().required(),
569
});
570
571
// Custom locale for specific domain
572
setLocale({
573
string: {
574
min: "Password must be at least ${min} characters long",
575
matches: "Password must contain uppercase, lowercase, and numbers",
576
},
577
});
578
579
// Reset to default locale
580
setLocale(defaultLocale);
581
```
582
583
### Custom Methods
584
585
Extend schema types with custom validation methods and functionality.
586
587
```typescript { .api }
588
/**
589
* Add custom methods to schema types
590
* @param schemaType - Schema constructor to extend
591
* @param methodName - Name of the method to add
592
* @param method - Implementation function
593
*/
594
function addMethod<T extends Schema>(
595
schemaType: new (...args: any[]) => T,
596
methodName: string,
597
method: (this: T, ...args: any[]) => T
598
): void;
599
```
600
601
**Usage Examples:**
602
603
```typescript
604
import { addMethod, string, StringSchema } from "yup";
605
606
// Add custom method to StringSchema
607
addMethod(StringSchema, "strongPassword", function() {
608
return this.test(
609
"strong-password",
610
"Password must contain uppercase, lowercase, number, and special character",
611
(value) => {
612
if (!value) return false;
613
return /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/.test(value);
614
}
615
);
616
});
617
618
// Extend TypeScript interface for type safety
619
declare module "yup" {
620
interface StringSchema {
621
strongPassword(): StringSchema;
622
}
623
}
624
625
// Use custom method
626
const passwordSchema = string().min(8).strongPassword().required();
627
628
// Add method with parameters
629
addMethod(StringSchema, "containsWord", function(word: string) {
630
return this.test(
631
"contains-word",
632
`Must contain the word "${word}"`,
633
(value) => value?.includes(word) ?? false
634
);
635
});
636
637
declare module "yup" {
638
interface StringSchema {
639
containsWord(word: string): StringSchema;
640
}
641
}
642
643
const titleSchema = string().containsWord("yup").required();
644
645
// Add method to NumberSchema
646
addMethod(NumberSchema, "currency", function() {
647
return this.test(
648
"currency",
649
"Must be a valid currency amount",
650
(value) => {
651
if (value == null) return true;
652
return Number.isFinite(value) && Math.round(value * 100) === value * 100;
653
}
654
).transform((value) => {
655
return value != null ? Math.round(value * 100) / 100 : value;
656
});
657
});
658
659
declare module "yup" {
660
interface NumberSchema {
661
currency(): NumberSchema;
662
}
663
}
664
665
const priceSchema = number().positive().currency().required();
666
```
667
668
### Advanced Utilities
669
670
Additional utility functions and configurations for specialized use cases.
671
672
```typescript { .api }
673
/**
674
* Create ValidationError instances programmatically
675
* @param errors - Error messages or ValidationError instances
676
* @param value - Value that failed validation
677
* @param field - Field path where error occurred
678
* @param type - Type of validation error
679
* @returns ValidationError instance
680
*/
681
function createError(
682
errors: string | string[] | ValidationError | ValidationError[],
683
value?: any,
684
field?: string,
685
type?: string
686
): ValidationError;
687
688
/**
689
* Global configuration options for yup behavior
690
*/
691
interface YupConfig {
692
/** Default strict mode setting */
693
strict?: boolean;
694
/** Default abort early setting */
695
abortEarly?: boolean;
696
/** Default strip unknown setting */
697
stripUnknown?: boolean;
698
}
699
700
/**
701
* Configure global yup defaults
702
* @param config - Global configuration options
703
*/
704
function configure(config: YupConfig): void;
705
```
706
707
**Usage Examples:**
708
709
```typescript
710
import { createError, configure, ValidationError } from "yup";
711
712
// Create custom validation errors
713
const customError = createError(
714
"Custom validation failed",
715
"invalid-value",
716
"field.path",
717
"custom"
718
);
719
720
// Multiple errors
721
const multipleErrors = createError([
722
"First error message",
723
"Second error message"
724
], null, "field");
725
726
// Global configuration
727
configure({
728
strict: false,
729
abortEarly: false,
730
stripUnknown: true,
731
});
732
733
// Custom error aggregation utility
734
function aggregateErrors(errors: ValidationError[]): Record<string, string[]> {
735
const result: Record<string, string[]> = {};
736
737
errors.forEach(error => {
738
error.inner.forEach(innerError => {
739
if (innerError.path) {
740
if (!result[innerError.path]) {
741
result[innerError.path] = [];
742
}
743
result[innerError.path].push(innerError.message);
744
}
745
});
746
});
747
748
return result;
749
}
750
```