Skip to content

Getting Started ​

Get up and running with Property Validator in minutes.

Installation ​

Clone the Repository ​

bash
git clone https://github.com/tuulbelt/property-validator.git
cd property-validator

Install Dev Dependencies ​

bash
npm install
npm link     # Enable the 'propval' command globally

Zero Runtime Dependencies

Property Validator has zero runtime dependencies. The npm install step only installs development tools (TypeScript compiler, test runner). The tool itself uses only Node.js built-in modules.

Bundle size: 30KB minified, 8KB gzipped

Quick Example ​

Basic Validation (Named Imports) ​

typescript
import { object, string, number, validate } from '@tuulbelt/property-validator';

// Define a schema with built-in validators
const UserSchema = object({
  name: string().min(1),
  age: number().positive(),
  email: string().email()
});

// Validate unknown data
const unknownData = {
  name: "Alice",
  age: 30,
  email: "alice@example.com"
};

const result = validate(UserSchema, unknownData);

if (result.ok) {
  // TypeScript knows the exact type
  console.log(`User: ${result.value.name}, Age: ${result.value.age}`);
} else {
  console.error(`Validation failed: ${result.error.message}`);
}

Namespace Import (Fluent API) ​

typescript
import { v, validate } from '@tuulbelt/property-validator/v';

const UserSchema = v.object({
  name: v.string(),
  age: v.number(),
  email: v.string()
});

Three Ways to Validate ​

Property Validator offers three APIs for different use cases:

validate() - Full Details ​

Use when you need error messages (forms, APIs):

typescript
import { object, string, validate } from '@tuulbelt/property-validator';

const UserSchema = object({ name: string().min(1) });
const result = validate(UserSchema, data);

if (result.ok) {
  console.log(result.value.name);  // Type-safe
} else {
  console.error(result.error.message);
  console.error(result.error.path);  // ["name"]
}

check() - Fast Boolean ​

Use when you only need pass/fail:

typescript
import { object, string, check } from '@tuulbelt/property-validator';

const UserSchema = object({ name: string() });

// 3x faster than validate() - skips error construction
if (check(UserSchema, data)) {
  processUser(data);
}

// Great for filtering
const validUsers = users.filter(u => check(UserSchema, u));

compileCheck() - Maximum Speed ​

Use for hot paths and large datasets:

typescript
import { object, number, compileCheck } from '@tuulbelt/property-validator';

const PointSchema = object({ x: number(), y: number() });
const isValidPoint = compileCheck(PointSchema);  // Compile once

// Use in performance-critical loops
for (const point of points) {
  if (isValidPoint(point)) {
    render(point);
  }
}

Built-in Validators ​

String Validators ​

typescript
import { string } from '@tuulbelt/property-validator';

// Format validation
const email = string().email();
const website = string().url();
const id = string().uuid();

// Length constraints
const username = string().min(3).max(20);
const code = string().length(6);

// Pattern matching
const phone = string().pattern(/^\+?[1-9]\d{1,14}$/);

Number Validators ​

typescript
import { number } from '@tuulbelt/property-validator';

// Sign constraints
const positive = number().positive();
const nonNegative = number().nonnegative();

// Bounds
const percentage = number().range(0, 100);
const port = number().int().range(1, 65535);

// Integer only
const count = number().int();

Optional and Nullable ​

typescript
import { object, string, number, optional, nullable } from '@tuulbelt/property-validator';

const UserSchema = object({
  name: string(),
  age: optional(number()),     // Can be undefined
  bio: nullable(string()),     // Can be null
  nickname: string().nullish() // Can be undefined or null
});

// All valid:
validate(UserSchema, { name: "Alice" });
validate(UserSchema, { name: "Alice", age: undefined, bio: null });
validate(UserSchema, { name: "Alice", age: 30, bio: "Developer" });

Default Values ​

typescript
import { object, string, number, boolean, validate } from '@tuulbelt/property-validator';

const ConfigSchema = object({
  port: number().default(3000),
  host: string().default('localhost'),
  debug: boolean().default(false)
});

const result = validate(ConfigSchema, {});
console.log(result.value);
// { port: 3000, host: 'localhost', debug: false }

Nested Objects ​

typescript
import { object, string, array, validate } from '@tuulbelt/property-validator';

const AddressSchema = object({
  street: string(),
  city: string(),
  zip: string().pattern(/^\d{5}$/)
});

const UserSchema = object({
  name: string().min(1),
  addresses: array(AddressSchema)
});

const result = validate(UserSchema, {
  name: "Charlie",
  addresses: [{
    street: "123 Main St",
    city: "Springfield",
    zip: "12345"
  }]
});

Understanding Results ​

Property Validator returns a result type instead of throwing exceptions:

Success Result ​

typescript
{
  ok: true,
  value: <validated data>  // Type matches schema
}

Error Result ​

typescript
{
  ok: false,
  error: {
    message: string,           // Human-readable error
    path: (string | number)[], // Path to invalid field
    expected: string,          // Expected type
    actual: string,            // Actual type
    format(style): string      // Format as 'text', 'json', or 'color'
  }
}

Error Handling Example ​

typescript
import { object, string, number, validate } from '@tuulbelt/property-validator';

const UserSchema = object({
  name: string().min(1),
  age: number().positive()
});

const result = validate(UserSchema, {
  name: "Alice",
  age: "thirty"  // Invalid: string instead of number
});

if (!result.ok) {
  console.log(result.error.message);
  // "Validation failed at age: expected number, got string"

  console.log(result.error.path);
  // ["age"]

  console.log(result.error.format('text'));
  // "Validation failed at age: expected number, got 'thirty'"
}

Next Steps ​

Released under the MIT License.