JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 9
  • Score
    100M100P100Q14271F
  • License MIT

Object validation tools for Javascript and, specially, Typescript

Package Exports

  • shapeit

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (shapeit) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

shapeit

shapeit is an object validation tools for Javascript and, specially, Typescript. With it, you can ensure any javascript object has a provided shape corresponding to a typescript type. You can also do asynchronous data validation of any nested object and get decent error messages.

Usage

shapeit consists of two different parts.

The first one is called guards and is dedicated to typecheking, so, its synchronous all the way down since typescript demands.

The second one is called validation and is dedicated to apply rules to a typechecked input. Since validation can be asynchronous, all the rules are applied asynchronously.

Guards

A basic example of guards usage would be like this

const sp = require('shapeit');

// First we create a guard
const personShape = sp.shape({
    name: 'string',
    age: 'number',
});

// Then we create some objects (consider they as unknown types)
const p1 = {
    name: 'John',
    age: 26
};

const p2 = {
    name: 'Mary',
    age: '27'
};

// Then we test then against the created shape
if (personShape(p1)) {
    p1; // p1 is now typed as { name: string; age: number }
}

personShape.errors; // null

if (personShape(p2)) {
    p2; // This line is not executed since p2 doesn't fit the shape
}

personShape.errors; // { '$.age': [ "Invalid type provided. Expected: 'number'" ] }

And a more complex one

import * as sp from 'shapeit';

const personShape = sp.shape({
    name: 'string',
    age: 'number',
    emails: sp.arrayOf('string')
});

const person = {
    name: 'John Doe',
    age: 25,
    emails: [
        'john.doe@example.com',
        'john_doe@email.com',
        null
    ]
};

if (personShape(person)) {
    person; // Unexecuted line
}

personShape.errors; // { '$.emails.2': [ "Invalid type provided. Expected: 'string'" ] }

Validation

Validation can be a little bit trickier. It consists of a set of rules, which are functions that receive the object and an assert function. The trick here is that it works deeply.

A simple example would be like this

const sp = require('shapeit');

// First, we get a valid typed object
const person = {
    name: 'Not John Doe',
    age: 25
};

// Then we can validate it
sp.validate(person, {
    name: (name, assert) => {
        assert(name === 'John Doe', 'You must be John Doe');
    },
    age: (age, assert) => {
        assert(age >= 18, 'You must be at least 18 years old');
    }
}).then(result => {
    result.valid; // false
    result.errors; // { '$.name': ['You must be John Doe'] }
});

And a more complex one would be

import * as sp from 'shapeit';

// Typescript interface (you can obtain that with guards too)
interface Person {
    name: string;
    age: number;
    emails: string[];
    // Notice "job" is an optional parameter
    job?: {
        id: number;
        boss_id: number;
    }
}

const person: Person = {
    name: 'John Doe',
    age: 25,
    emails: [
        'john.doe@example.com',
        'john_doe@email.com'
    ],
    job: {
        id: 13,
        boss_id: 10
    }
};

validate(person, {
    name: (name, assert) => {
        assert(name === 'John Doe', 'You must be John Doe');
    },
    age: (age, assert) => {
        assert(age >= 18, 'You must be at least 18 years old');
    },
    // An object validator can be an object with its keys
    job: {
        // Those rules will be evaluated only if key "job"
        // exists in the person object. So, don't need to
        // worry about that
        id: async (job_id, assert) => {
            assert(
                await existsOnDb(job_id),
                'This job doesnt exist on database'
            )
        },
        // Rules can be asynchronous functions 🥳
        // and all of them will be executed in parallel
        boss_id: async (job_id, assert) => {
            assert(
                await existsOnDb(job_id),
                'This employee doesnt exist on database'
            )
        }
    },
    // When you need to validate the entire object and its keys,
    // you can pass an array containing
    // its rule and the rules for its members
    emails: [
        (emails, assert) => {
            assert(emails.length > 0, 'Provide at least one email');
        },
        {
            // $each is a way to apply the same rule
            // to all the array elements
            $each: (email, assert) => {
                assert(isValidEmail(email), 'Invalid email');
            }
        }
    ]
}).then(result => {
    // Do something with validation result
});

This way, you can set schemas to validate all your incoming data with typesafety and get error messages matching the fields of your object.