Blork! Mini runtime type checking in Javascript
A mini type checker for locking down the external edges of your code. Mainly for use in modules when you don"t know who'll be using the code. Minimal boilerplate code keeps your functions hyper readable and lets them be their beautiful minimal best selves (...or something?)
Blork is fully unit tested and 100% covered (if you're into that!).
Installation
npm install blork
Usage
args(): Check function arguments
The primary use case of Blork is validating function input arguments. The args()
function is provided for this purpose, and should be passed two arguments:
arguments
| The arguments object provided automatically to functions in Javascripttypes
| An array identifying the types for the arguments (list of types is available below)
import { args } from "blork";
export default function myFunc(definitelyString, optionalNumber)
{
args(arguments, ["string", "number?"]);
return "It passed!";
}
myFunc("abc", 123);
myFunc("abc");
myFunc(123);
myFunc("abc", "abc");
myFunc();
myFunc("abc", 123, true);
check(): Check individual values
The check()
function allows you to test individual values with more granularity. The check()
function is more versatile and allows more use cases than validating function input arguments.
check()
can be passed three arguments:
value
| The value to checktype
| The type to check the value against (list of types is available below)- An optional string name for the value, which is prepended to any error message thrown to help debugging
import { check } from "blork";
check("Sally", "string");
check("Sally", String);
check("Sally", "number");
check("Sally", Boolean);
check("Sally", "num", "name");
check(true, "str", "status");
Checking optional values
Appending ?
question mark to any type string makes it optional. This means it will accept undefined
in addition to the specified type.
check(undefined, "number");
check(undefined, "number?");
check(null, "number?");
check()
and args()
return the number of defined values that passed. i.e. If a check passes because it"s optional it will return 0
as shown above.
Checking objects and arrays
Blork can perform deep checks on objects and arrays to ensure the schema is correct. To do object or array checks pass literal arrays or literal objects to check()
or args()
:
check({ name: "Sally" }, { name: "string" });
check(["Sally", "John", "Sonia"], ["str"]);
check([1029, "Sonia"], ["number", "string"]);
check({ name: "Sally" }, { name: "string" });
check(["Sally", "John", "Sonia"], ["str"]);
check([1029, "Sonia"], ["number", "string"]);
check([1029, "Sonia", true], ["number", "string"]);
Arrays and objects can be deeply nested within each other and Blork will recursively check the schema all the way down:
check(
[
{ id: 1028, name: "Sally", status: [1, 2, 3] },
{ id: 1062, name: "Bobby", status: [1, 2, 3] }
],
[
{ id: Number, name: String, status: [Number] }
]
);
check(
[
{ id: 1028, name: "Sally", status: [1, 2, 3] },
{ id: 1062, name: "Bobby", status: [1, 2, "not_a_number"] }
],
[
{ id: Number, name: String, status: [Number] }
]
);
add(): Add a custom checker type
Register your own checker using the add()
function. If you're going to be applying the same check over and over and want a custom type that's integrated with Blork's built-in types and has consistent error messages, this is a great way to go.
import { add, check } from "blork";
add("catty", (v) => {
return typeof v === "string" && v.indexOf("cat") >= 0 || "Must be a string containing 'cat'";
});
check("That cat is having fun", "catty");
check("A dog sits on the chair", "catty");
import { add, args } from "blork";
function myFunc(str)
{
args(arguments, ["catty"]);
return "It passed!";
}
myFunc("That cat is chasing string");
myFunc("A dog sits over there");
throws(): Set a custom error constructor
To change the error object Blork throws when a type doesn't match, use the throws()
function.
import { throws, check } from "blork";
class MyError extends Error {};
throws(MyError);
check(true, "false");
blork(): Create an independent instance of Blork
To create an instance of Blork with an independent set of checkers (added with add()
) and an independently set throws()
error object, use the blork()
function.
This functionality is provided so you can ensure multiple versions of Blork in submodules of the same project don't interfere with each other, even if they have been (possibly purposefully) deduped in npm.
import { blork } from "blork";
const { check, add, throws } = blork();
throws(class CustomError extends TypeError);
add("mychecker", v => v === "abc" || "Must be 'abc'");
check("123", "mychecker");
Types
String types
Types are generally accessed via a string reference. This list shows all Blork built-in checkers:
Type string reference | Description |
---|
null | Value is null |
undefined , undef , void | Value is undefined |
defined , def | Value is not undefined |
boolean , bool | Value is true or false |
true | Value is true |
false | Value is false |
truthy | Any truthy values (i.e. == true) |
falsy | Any falsy values (i.e. == false) |
number , num | Numbers excluding NaN/Infinity (using typeof and finite check) |
number+ , num+ | Numbers more than or equal to zero |
number- , num- | Numbers less than or equal to zero |
integer , int | Integers (using Number.isInteger()) |
integer+ , int+ | Positive integers including zero |
integer- , int- | Negative integers including zero |
string , str | Strings (using typeof) |
string+ , str+ | Non-empty strings (using str.length) |
lowercase , lower | Strings with no uppercase characters |
lowercase+ , lower+ | Non-empty strings with no uppercase characters |
uppercase , upper | Strings with no lowercase characters |
uppercase+ , upper+ | Non-empty strings with no lowercase characters |
function , func | Functions (using instanceof Function) |
object , obj | Plain objects (using instanceof Object and constructor check) |
object+ , obj+ | Plain objects with one or more properties (using Object.keys().length) |
objectlike | Any object-like object (using instanceof Object) |
iterable | Objects with a Symbol.iterator method (that can be used with for..of loops) |
array , arr | Plain instances of Array (using instanceof Array and constructor check) |
array+ , arr+ | Plain instances of Array with one or more items |
arraylike | Any object, not just arrays, with numeric .length property |
arguments , args | Arguments objects (any object, not just arrays, with numeric .length property) |
map | Instances of Map |
map+ | Instances of Map with one or more items |
weakmap | Instances of WeakMap |
set | Instances of Set |
set+ | Instances of Set with one or more items |
weakset | Instances of WeakSet |
promise | Instances of Promise |
date | Instances of Date |
date+ , future | Instances of Date with a value in the future |
date- , past | Instances of Date with a value in the past |
regex , regexp | Instances of RegExp (regular expressions) |
any , mixed | Allow any value (transparently passes through with no error) |
check("abc", "str");
check("abc", "lower");
check(100, "whole");
check([1, 2, 3], "array+");
check(new Date(2180, 1, 1), "future");
check(123, "str");
check({}, "object+");
check([], "array+");
Optional string types
Any type can be made optional by appending a ?
question mark to the type reference. This means the check will also accept undefined
in addition to the specified type.
Note: If the check passes because the value was optional (and undefined
was received), check()
and args()
do not increment their return count (of defined values), and will return 1.
check(undefined, "str?");
check(undefined, "lower?");
check(undefined, "whole?");
check([undefined, undefined, 123], ["number?"]);
check(123, "str?");
check(null, "str?");
Constructor and constant types
For convenience some constructors (e.g. String
) and constants (e.g. null
) can be used as types in args()
and check()
. The following built-in objects and constants are supported:
Type | Description |
---|
Boolean | Same as 'boolean' type |
String | Same as 'string' type |
Number | Same as 'number' type |
true | Same as 'true' type |
false | Same as 'false' type |
null | Same as 'null' type |
undefined | Same as 'undefined' type |
You can pass in any class name, and Blork will check the value using instanceof
and generate a corresponding error message if the type doesn't match.
Using Object
and Array
constructors will work also and will allow any object that is instanceof Object
or instanceof Array
. Note: this is not the same as e.g. the 'object'
and 'array'
string types, which only allow plain objects an arrays (but will reject objects of custom classes extending Object
or Array
).
check(true, Boolean);
check("abc", String);
check(123, Number);
check(new Date, Date);
check(new MyClass, MyClass);
check(Promise.resolved(true), Promise);
check([true, true, false], [Boolean]);
check({ name: 123 }, { name: Number });
check("abc", Boolean);
check("abc", String);
check("abc", String, "myVar");
check(new MyClass, OtherClass);
check({ name: 123 }, { name: String });
check({ name: 123 }, { name: String }, "myObj");
Object literal type
To check the types of object properties, use a literal object as a type. You can also deeply nest these properties and the types will be checked recursively and will generate useful debuggable error messages.
Note: it is fine for objects to contain additional properties that don't have a type specified.
check({ name: "abc" }, { name: "str" });
check({ name: "abc" }, { name: "str?", age: "num?" });
check({ name: "abc", additional: true }, { name: "str" });
check({ age: "apple" }, { age: "num" });
check({ size: { height: 10, width: "abc" } }, { size: { height: "num", width: "num" } });
Object literal type (with additional properties)
To check that the type of all properties in an object all conform to a type, use an ANY
key. This allows you to check objects that don't have known keys (e.g. from user generated data). This is similar to how indexer keys work in Flow or Typescript.
import { check, ANY } from "blork";
check({ a: 1, b: 2, c: 3 }, { [ANY]: "num" });
check({ name: "Dan", a: 1, b: 2, c: 3 }, { name: "str", [ANY]: "num" });
check({ a: 1, b: 2, c: "abc" }, { [ANY]: "num" });
If you wish you can use this functionality with the undefined
type to ensure objects do not contain additional properties (object literal types by default are allowed to contain additional properties).
check({ name: "Carl" }, { name: "str", [ANY]: "undefined" });
check({ name: "Jess", another: 28 }, { name: "str", [ANY]: "undefined" });
Array literal type
To check an array where all items conform to a specific type, pass an array as the type. Arrays and objects can be deeply nested to check types recursively.
check(["abc", "abc"], ["str"]);
check([123, 123], ["num"]);
check([{ names: ["Alice", "John"] }], [{ names: ["str"] }]);
check(["abc", "abc", 123], ["str"]);
check(["abc", "abc", 123], ["number"]);
Array tuple type
Similarly, to check the format of tuples, pass an array with two or more items as the type. If two or more types are in an type array, it is considered a tuple type and will be rejected if it does not conform exactly to the tuple.
check([123, "abc"], ["num", "str"]);
check([123, "abc"], ["num", "str", "str?"]);
check([123], ["num", "str"]);
check([123, 123], ["num", "str"]);
check([123, "abc", true], ["num", "str"]);
Contributing
Please see (CONTRIBUTING.md)
Roadmap