pico-type

1.0.0 • Public • Published

🛂 pico-type

A tiny custom interface checker for js. Easily describe the shape of your data and validate it.

install

npm install pico-type

Example

const {or, opt, is, ensure} = require('pico-type');

const UserType = {
	id         : types.uuid,
	email      : /^.+@.+\..+$/,
	post_count : Number,
	tags       : [String],
	posts : [{
		title : String,
		read : Function
	}],
	age      : (val)=>0 < val && val < 150,
	flagged  : opt(Boolean),
	created  : or(Number, Date),
	location : types.geo,
	meta     : '*'
}

const user = {...};

if(is(UserType, user)) console.log('is a user!');

ensure(UserType, user); //Throws if not a user

const isUserType = is.bind(null, UserType);

isUserType(user);  // true/false checker for UserTypes now!

Features

  • Interfaces are just simple objects, arrays, functions, or Native primitives
  • Under 100 lines of code
  • Able to disable run-time checks for production/performance
  • Can use .cast() to create proxies to do assertion-time checks

pico-type gives you tools for checking data strucutres against interfaces to see if they pass. Define the shape of your data using Native types, functions, arrays, objects, or regex.

How To Use

You define interfaces using a combination of the below types:

Wildcard

If your interface is exactly '*', picotype will pass regardless of value.

ensure('*', 'foo')

Natives

Any Native Type in js can be used: String, Number, Function, Date, Object, Array, etc.

const { ensure } = require('pico-type');

ensure(String, "Oh hello");
ensure(Object, {a : true});
ensure(Number, 500);

Regex

You can use a Regular Expression as a type, and pico-type will use it to test against the value.

const { ensure } = require('pico-type');

ensure(/^.+@.+\..+$/, 'john.smith@test.com');
ensure(/^\+?(\d.*){3,}$/, '613-555-0106');

Array

If you provide an array as a type, pico-type will check that the value is an array, and use the first element of the interface array as the interface for every element that is in the value's corresponding array

const { ensure } = require('pico-type');

ensure([String], ['these', 'are', 'tags']);
ensure([{ ok : Boolean }], [{ ok : true}, { ok : false}]);

Objects

Providing an object as a type will iterate over each key, and uses it's value as a interface for the value's corresponding key.

const { ensure } = require('pico-type');

ensure({ ok : Boolean, foo : Number}, {ok : true, foo : 6});

//These can be nested
ensure({
	nested : {
		obj : String
	}
}, { nested : { obj : 'doot'}});

This also lets you easily combine interfaces together.

const AgeType = (val)=>0<val && val<150
const TagType = {
	tag : String,
	ts : or(Date, Number)
};

const UserType = {
	name: String,
	age : AgeType,
	tags : [TagType]
}

const UserGroupType = {
	id : String,
	users : [UserType],
};

Function

Any function can be used as a type. pico-type will call the function while validating. If the function returns exactly true or undefined it's considered a pass. If it returns anything else (or throws an error), it's considered a fail and pico-type will use the returned value as the error message.

const { ensure } = require('pico-type');

const ageType = (val)=>0 < val && val < 150
ensure(ageType, 36);

//Picotype passes the arg name as the second paremter, which you can use in your error messages.
const geoType = (val, name)=>{
	if(typeof val.lat !== 'number') return `${name}.lat is not a number`;
	if(typeof val.lng !== 'number') return `${name}.lng is not a number`;
	if(val.lat < -90 || val.lat > 90) return `${name}.lat has an invalid range`;
	if(val.lng < -180 || val.lng > 180) return `${name}.lng has an invalid range`;
}
ensure(geoType, {lat: -29.83249, lng: -50.66126});

API

.ensure(interface, value) -> true / Error

Returns true if the value passes the interface, otherwise throws with an error explaining which field invalidates the interface and why.

.is(interface, value) -> true / false

Returns true if the value matches the interface, false if it doesn't

.enable() / .disable()

Enables or disables pico-type's checks. This makes .ensure(), .is(), .wrap(), and .cast() perform fast no-ops

.wrap(parameterInterfaces, func, returnInterface) -> function

Wraps a given function in both parameter and return checks, returns a new veried function.

parameterInterfaces is an array of interfaces applied to each argument

const {wrap} = require('pico-type');
const GeoType = {/*...*/}


const geoDistance = wrap(
	[GeoType, GeoType],
	(point1,    point2)=>{/*...*/},
	Number
);

//can leave either parameter or return interface blank as well
const noChecks = wrap(null, (a,b,c)=>{}, null);

.cast(interface, [initVal])

Creates a Proxied object that whenever a property is set on it, it checks against the provided interface.

let tempUser = cast({
	name : String,
	age : AgeType
});

tempUser.name = true; //Throws an error!
tempUser.age = -2.3452; //Also throws an error!

You can also pass an initial value into the proxied Object as the second parameter. Under the hood, pico-type will skip checks on cast'd objects if they are combine in other checks, since their are guaranteed to be passing!

Utils

.or(...types) -> function(value)

Returns a validation function that passes if the value passes any one of the provided types.

const {or, ensure} = require('pico-type');

const DateType = or(Number, Date);
ensure(DateType, new Date());

.opt(type) -> function(value)

Returns a validation function that passes if the value passes the type or is undefined

const {opt, ensure} = require('pico-type');

const BoolOrNothing = opt(Boolean);

Readme

Keywords

none

Package Sidebar

Install

npm i pico-type

Weekly Downloads

2

Version

1.0.0

License

MIT

Unpacked Size

16.6 kB

Total Files

12

Last publish

Collaborators

  • stolksdorf