or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdschema-types.mdutilities.mdvalidation.md

validation.mddocs/

0

# Validation & Testing

1

2

Comprehensive validation system with async/sync validation, custom tests, conditional validation, and detailed error handling for robust data validation scenarios.

3

4

## Capabilities

5

6

### Core Validation Methods

7

8

Primary methods for validating data against schemas, available on all schema types.

9

10

```typescript { .api }

11

/**

12

* Asynchronously validate a value against the schema

13

* @param value - Value to validate

14

* @param options - Validation configuration options

15

* @returns Promise resolving to validated and transformed value

16

* @throws ValidationError if validation fails

17

*/

18

validate(value: any, options?: ValidateOptions): Promise<T>;

19

20

/**

21

* Synchronously validate a value against the schema

22

* @param value - Value to validate

23

* @param options - Validation configuration options

24

* @returns Validated and transformed value

25

* @throws ValidationError if validation fails

26

*/

27

validateSync(value: any, options?: ValidateOptions): T;

28

29

/**

30

* Validate a nested field at a specific path within an object

31

* @param path - Dot-notation path to the field

32

* @param value - Root object containing the field

33

* @param options - Validation configuration options

34

* @returns Promise resolving to validated field value

35

*/

36

validateAt(path: string, value: any, options?: ValidateOptions): Promise<any>;

37

38

/**

39

* Synchronously validate a nested field at a specific path

40

* @param path - Dot-notation path to the field

41

* @param value - Root object containing the field

42

* @param options - Validation configuration options

43

* @returns Validated field value

44

*/

45

validateSyncAt(path: string, value: any, options?: ValidateOptions): any;

46

47

/**

48

* Check if a value is valid without throwing errors

49

* @param value - Value to check

50

* @param options - Validation configuration options

51

* @returns Promise resolving to boolean validity status

52

*/

53

isValid(value: any, options?: ValidateOptions): Promise<boolean>;

54

55

/**

56

* Synchronously check if a value is valid without throwing errors

57

* @param value - Value to check

58

* @param options - Validation configuration options

59

* @returns Boolean validity status

60

*/

61

isValidSync(value: any, options?: ValidateOptions): boolean;

62

63

/**

64

* Check if a value matches the schema's expected type

65

* @param value - Value to type-check

66

* @returns Type predicate indicating if value matches schema type

67

*/

68

isType(value: any): value is T;

69

70

interface ValidateOptions<TContext = {}> {

71

/** Enable strict validation (no type coercion) */

72

strict?: boolean;

73

/** Stop validation on first error (default: true) */

74

abortEarly?: boolean;

75

/** Remove unknown fields from objects */

76

stripUnknown?: boolean;

77

/** Validate nested schemas recursively */

78

recursive?: boolean;

79

/** Additional context data available during validation */

80

context?: TContext;

81

}

82

```

83

84

**Usage Examples:**

85

86

```typescript

87

import { object, string, number } from "yup";

88

89

const userSchema = object({

90

name: string().required(),

91

age: number().positive().integer(),

92

});

93

94

// Async validation

95

try {

96

const user = await userSchema.validate({

97

name: "John",

98

age: 25

99

});

100

console.log(user); // { name: "John", age: 25 }

101

} catch (error) {

102

console.error(error.errors); // Array of error messages

103

}

104

105

// Sync validation

106

try {

107

const user = userSchema.validateSync({ name: "John", age: 25 });

108

} catch (error) {

109

console.error(error.message);

110

}

111

112

// Check validity without throwing

113

const isValid = await userSchema.isValid({ name: "John", age: -5 }); // false

114

115

// Validate nested field

116

await userSchema.validateAt("name", { name: "John", age: 25 }); // "John"

117

```

118

119

### Value Casting

120

121

Transform and coerce values to match schema types without full validation.

122

123

```typescript { .api }

124

/**

125

* Cast/transform a value to match the schema type

126

* @param value - Value to cast

127

* @param options - Casting configuration options

128

* @returns Transformed value matching schema type

129

*/

130

cast(value: any, options?: CastOptions): T;

131

132

interface CastOptions<TContext = {}> {

133

/** Enable strict casting (minimal transformations) */

134

strict?: boolean;

135

/** Remove unknown fields from objects */

136

stripUnknown?: boolean;

137

/** Additional context data available during casting */

138

context?: TContext;

139

}

140

```

141

142

**Usage Examples:**

143

144

```typescript

145

import { string, number, date } from "yup";

146

147

// String casting

148

const stringSchema = string();

149

stringSchema.cast(123); // "123"

150

stringSchema.cast(true); // "true"

151

152

// Number casting

153

const numberSchema = number();

154

numberSchema.cast("42"); // 42

155

numberSchema.cast("3.14"); // 3.14

156

157

// Date casting

158

const dateSchema = date();

159

dateSchema.cast("2023-01-01"); // Date object

160

dateSchema.cast(1640995200000); // Date from timestamp

161

```

162

163

### Custom Validation Tests

164

165

Add custom validation logic with detailed error reporting and context access.

166

167

```typescript { .api }

168

/**

169

* Add a custom validation test to the schema

170

* @param name - Test name for error reporting

171

* @param message - Error message when test fails

172

* @param testFn - Function that returns true if valid

173

* @returns New schema with custom test

174

*/

175

test(name: string, message: string, testFn: TestFunction): Schema;

176

177

/**

178

* Add a custom validation test with full configuration

179

* @param options - Complete test configuration

180

* @returns New schema with custom test

181

*/

182

test(options: TestConfig): Schema;

183

184

type TestFunction<T = any, C = any> = (

185

value: T,

186

context: TestContext<T, C>

187

) => boolean | Promise<boolean>;

188

189

interface TestConfig<T = any, C = any> {

190

/** Unique name for the test */

191

name: string;

192

/** Error message when test fails (string or function) */

193

message: string | ((params: TestMessageParams) => string);

194

/** Test function that validates the value */

195

test: TestFunction<T, C>;

196

/** Skip test if value is empty/null/undefined */

197

skipAbsent?: boolean;

198

/** Test is exclusive - removes other tests with same name */

199

exclusive?: boolean;

200

/** Parameters to pass to error message function */

201

params?: Record<string, any>;

202

}

203

204

interface TestContext<T = any, C = any> {

205

/** Path to current field being validated */

206

path: string;

207

/** Field name being validated */

208

key?: string;

209

/** Parent object containing the field */

210

parent: any;

211

/** Root value being validated */

212

from: Array<{ schema: Schema; value: any }>;

213

/** Additional context data */

214

options: ValidateOptions<C>;

215

/** Create ValidationError for this test */

216

createError(params?: CreateErrorOptions): ValidationError;

217

/** Resolve references and lazy schemas */

218

resolve(value: any): any;

219

}

220

221

interface CreateErrorOptions {

222

/** Override default error message */

223

message?: string;

224

/** Override field path */

225

path?: string;

226

/** Override field value */

227

value?: any;

228

/** Additional parameters for message interpolation */

229

params?: Record<string, any>;

230

}

231

```

232

233

**Usage Examples:**

234

235

```typescript

236

import { string, number, object } from "yup";

237

238

// Simple custom test

239

const evenNumberSchema = number().test(

240

"is-even",

241

"Number must be even",

242

(value) => value % 2 === 0

243

);

244

245

// Async custom test with API call

246

const uniqueEmailSchema = string().test(

247

"unique-email",

248

"Email already exists",

249

async (email) => {

250

const exists = await checkEmailExists(email);

251

return !exists;

252

}

253

);

254

255

// Custom test with context access

256

const passwordSchema = object({

257

password: string().min(8),

258

confirmPassword: string().test(

259

"passwords-match",

260

"Passwords must match",

261

function(value) {

262

return value === this.parent.password;

263

}

264

),

265

});

266

267

// Custom test with dynamic message

268

const minLengthSchema = string().test({

269

name: "min-length",

270

message: ({ min }) => `Must be at least ${min} characters`,

271

params: { min: 5 },

272

test: (value) => value && value.length >= 5,

273

});

274

```

275

276

### Conditional Validation

277

278

Create dynamic validation rules based on other field values or conditions.

279

280

```typescript { .api }

281

/**

282

* Apply conditional validation based on other fields

283

* @param keys - Field name(s) to check for condition

284

* @param builder - Function or config that returns schema based on condition

285

* @returns New schema with conditional validation

286

*/

287

when<U extends Schema>(

288

keys: string | string[],

289

builder: WhenBuilder<U> | WhenBuilderOptions<U>

290

): Schema;

291

292

type WhenBuilder<U extends Schema> = (

293

value: any,

294

schema: Schema

295

) => U;

296

297

interface WhenBuilderOptions<U extends Schema> {

298

/** Value(s) to match against */

299

is?: any | any[];

300

/** Schema to use when condition matches */

301

then?: U | ((schema: Schema) => U);

302

/** Schema to use when condition doesn't match */

303

otherwise?: U | ((schema: Schema) => U);

304

}

305

```

306

307

**Usage Examples:**

308

309

```typescript

310

import { object, string, boolean, number } from "yup";

311

312

// Simple conditional validation

313

const schema = object({

314

isBusiness: boolean(),

315

companyName: string().when("isBusiness", {

316

is: true,

317

then: (schema) => schema.required("Company name is required"),

318

otherwise: (schema) => schema.strip(),

319

}),

320

});

321

322

// Multiple conditions

323

const subscriptionSchema = object({

324

type: string().oneOf(["free", "premium", "enterprise"]),

325

seats: number().when("type", {

326

is: "enterprise",

327

then: (schema) => schema.min(10).required(),

328

otherwise: (schema) => schema.max(5),

329

}),

330

customDomain: string().when("type", (type, schema) => {

331

return type === "premium" || type === "enterprise"

332

? schema.required()

333

: schema.strip();

334

}),

335

});

336

337

// Multiple field dependencies

338

const addressSchema = object({

339

country: string().required(),

340

state: string().when("country", {

341

is: "US",

342

then: (schema) => schema.required(),

343

}),

344

zipCode: string().when(["country", "state"], {

345

is: (country, state) => country === "US" && state,

346

then: (schema) => schema.matches(/^\d{5}$/, "Invalid ZIP code"),

347

}),

348

});

349

```

350

351

### Error Handling

352

353

Comprehensive error information with detailed validation failure reporting.

354

355

```typescript { .api }

356

/**

357

* Error thrown when validation fails

358

*/

359

class ValidationError extends Error {

360

/** Always "ValidationError" */

361

name: "ValidationError";

362

/** Primary error message */

363

message: string;

364

/** Value that failed validation */

365

value: any;

366

/** Path where validation failed */

367

path?: string;

368

/** Type of validation that failed */

369

type?: string;

370

/** Parameters used in error message */

371

params?: Record<string, any>;

372

/** Array of all error messages */

373

errors: string[];

374

/** Array of nested ValidationError objects */

375

inner: ValidationError[];

376

377

/**

378

* Format error message with parameters

379

* @param message - Message template

380

* @param params - Parameters for interpolation

381

* @returns Formatted message

382

*/

383

static formatError(

384

message: string | ((params: any) => string),

385

params: Record<string, any>

386

): string;

387

388

/**

389

* Check if an error is a ValidationError

390

* @param err - Error to check

391

* @returns Type predicate for ValidationError

392

*/

393

static isError(err: any): err is ValidationError;

394

}

395

```

396

397

**Usage Examples:**

398

399

```typescript

400

import { object, string, ValidationError } from "yup";

401

402

const schema = object({

403

name: string().required("Name is required"),

404

email: string().email("Invalid email format").required(),

405

});

406

407

try {

408

await schema.validate({ name: "", email: "not-email" });

409

} catch (error) {

410

if (ValidationError.isError(error)) {

411

console.log("Validation failed!");

412

console.log("Primary message:", error.message);

413

console.log("All errors:", error.errors);

414

console.log("Failed at path:", error.path);

415

console.log("Failed value:", error.value);

416

417

// Access nested errors

418

error.inner.forEach((nestedError) => {

419

console.log(`${nestedError.path}: ${nestedError.message}`);

420

});

421

}

422

}

423

424

// Custom error handling

425

const handleValidationError = (error: ValidationError) => {

426

const fieldErrors: Record<string, string> = {};

427

428

error.inner.forEach((err) => {

429

if (err.path) {

430

fieldErrors[err.path] = err.message;

431

}

432

});

433

434

return fieldErrors;

435

};

436

```

437

438

### Transformation Functions

439

440

Apply custom transformations to values during validation and casting.

441

442

```typescript { .api }

443

/**

444

* Add a transformation function to the schema

445

* @param transformFn - Function to transform the value

446

* @returns New schema with transformation applied

447

*/

448

transform<U>(transformFn: TransformFunction<T, U>): Schema<U>;

449

450

type TransformFunction<T, U> = (

451

currentValue: T,

452

originalValue: any,

453

context: TransformContext

454

) => U;

455

456

interface TransformContext {

457

/** Path to current field */

458

path: string;

459

/** Parent object */

460

parent: any;

461

/** Root value being transformed */

462

root: any;

463

/** Additional context data */

464

options: CastOptions;

465

}

466

```

467

468

**Usage Examples:**

469

470

```typescript

471

import { string, number, array } from "yup";

472

473

// String transformation

474

const slugSchema = string()

475

.transform((value) => value?.toLowerCase().replace(/\s+/g, "-"))

476

.matches(/^[a-z0-9-]+$/, "Invalid slug format");

477

478

// Number transformation

479

const currencySchema = string()

480

.transform((value) => {

481

// Remove currency symbols and convert to number

482

const cleaned = value?.replace(/[$,]/g, "");

483

return parseFloat(cleaned) || 0;

484

})

485

.transform((value) => Math.round(value * 100) / 100); // Round to 2 decimals

486

487

// Array transformation

488

const csvToArraySchema = string()

489

.transform((value) => value?.split(",").map(s => s.trim()))

490

.transform((arr) => arr?.filter(Boolean)); // Remove empty strings

491

492

// Context-aware transformation

493

const fullNameSchema = object({

494

firstName: string().required(),

495

lastName: string().required(),

496

fullName: string().transform(function(value, originalValue, context) {

497

// Auto-generate full name if not provided

498

if (!value && context.parent) {

499

return `${context.parent.firstName} ${context.parent.lastName}`;

500

}

501

return value;

502

}),

503

});

504

```

505

506

### Metadata and Labeling

507

508

Add metadata and labels to schemas for enhanced error reporting and documentation.

509

510

```typescript { .api }

511

/**

512

* Set or get metadata for the schema

513

* @param metadata - Metadata object to set (optional)

514

* @returns Schema metadata or new schema with metadata

515

*/

516

meta(): Record<string, any> | undefined;

517

meta(metadata: Record<string, any>): Schema;

518

519

/**

520

* Set a human-readable label for better error messages

521

* @param label - Human-readable field label

522

* @returns New schema with label

523

*/

524

label(label: string): Schema;

525

526

/**

527

* Set custom error message for type validation failures

528

* @param message - Custom type error message

529

* @returns New schema with custom type error

530

*/

531

typeError(message: string): Schema;

532

533

/**

534

* Get a description of the schema structure and rules

535

* @param options - Description options

536

* @returns Schema description object

537

*/

538

describe(options?: DescribeOptions): SchemaDescription;

539

540

interface DescribeOptions {

541

/** Include field values in description */

542

value?: any;

543

/** Additional context */

544

context?: any;

545

}

546

547

interface SchemaDescription {

548

/** Schema type */

549

type: string;

550

/** Human-readable label */

551

label?: string;

552

/** Schema metadata */

553

meta?: Record<string, any>;

554

/** Whether field is required */

555

optional: boolean;

556

/** Whether field is nullable */

557

nullable: boolean;

558

/** Default value */

559

default?: any;

560

/** Applied tests and validations */

561

tests: Array<{ name: string; params?: any }>;

562

/** Inner type for arrays/objects */

563

innerType?: SchemaDescription;

564

/** Object fields */

565

fields?: Record<string, SchemaDescription>;

566

}

567

```

568

569

**Usage Examples:**

570

571

```typescript

572

import { string, object } from "yup";

573

574

// Schema with labels and metadata

575

const userSchema = object({

576

email: string()

577

.email()

578

.required()

579

.label("Email Address")

580

.meta({

581

category: "contact",

582

sensitive: true,

583

helpText: "We'll use this to send you updates"

584

}),

585

586

username: string()

587

.matches(/^[a-zA-Z0-9_]+$/)

588

.required()

589

.label("Username")

590

.typeError("Username must be a string")

591

.meta({ category: "identity" }),

592

});

593

594

// Access metadata

595

const emailMeta = userSchema.fields.email.meta();

596

console.log(emailMeta.helpText); // "We'll use this to send you updates"

597

598

// Schema description

599

const description = userSchema.describe();

600

console.log(description.fields.email.label); // "Email Address"

601

console.log(description.fields.email.tests); // Array of validation tests

602

```