value-types

0.4.3 • Public • Published

value-types-js

A runtime type checking system for javascript.

value-types is designed to help javascript feel like a typed language. It encourages programmers to use explicit type definitions, and stays out of your way when you don't need it. It uses literal values as type definitions.

Introduction

value-types-js uses literal javascript values to define types. It checks these types at runtime. This means that value-types does not require any special syntax or static analysis.

Types are defined by creating a literal value that serves as an example of a defined type. These literal value type defintions may be:

  • Primitives
  • Structured Arrays
  • Objects with Properties
  • Objects as Class Instances

The precise rules for how types are defined and checked are described in the Usage section.

The goal of value-types is not to provide infallible type safety or accomodate infinitely nuanced type definitions. Instead, it is a minimal tool to help javascript programmers be explicit with the types they use, while avoiding verbosity and ceremony. Using value-types correctly will make your code more descriptive and less ambiguous. It will make many common bugs easier to find or avoid in the first place. You will be less vulnerable to mistakes like passing the wrong function arguments or trying to grab a property from a null object. Using value-types correctly can significantly improve your productivity and clarity when programming in javascript.

You can use the patterns defined by value-types to document your types even without loading the value-types library. Simply use an empty function in place one of the typechecking function defined by value-types. Your types won't be checked at run time, but it is still useful for documentation. You can also do this streamline your code execution for production without commenting out every typecheck.

##Usage

ValueTypes

  .type_check(types... x)
    returns true iff the value 'x' matches one or more of 'types'.

  .type_assert(types... x)
    If 'x' does not match any of 'types', throw an exception, otherwise return 'x'.

  .return_type_assert(types... f)
    wrap function f to assert the return value matches one or more of 'types'.

  .return_type_assert_bind(types... _this, f)  TODO
    wrap function f to assert the return value matches one or more of 'types',
    and specify '_this' as the 'this' value of the function.
    

  .T(types... x)
    Shorthand for '.type_assert(types... x)'

  .RT(types... f)
    Shorthand for '.return_type_assert(types... f)'

  .RT_bind(types... _this, f)
    Shorthand for '.return_type_assert_bind(types... _this, f)'
  • x is a value whose type we need to verify.
  • types (t1,t2...) are type definitions, forming a type union, only one of the types must match to succeed.
  • f a function to wrap, for checking returning values whenever it is called.

The value x is checked against the type definition t according to the following rules:

Primitives

###t is null

  • x must be null

###t is undefined

  • x must be undefined

###t is a primitive

  • x must match the primitive type of t.

###t is a function

  • x must be a function.

t is a regex

  • x must be a string that matches regex t.

Structured Arrays

###t is a list

  • length 0
    • x may be any list. (Any length or entry types allowed).
  • length 1
    • x may be a list of any length, but all entries of x must match the type of the single entry in t.
  • length 2 or more:
    • x must be a list with the same length as t, the type of each entry of x must match the type of the corresponding entry in t. These fixed length arrays could be thought of as n-tuple datastructures.

Objects

Objects with Properties: if t uses the default object constructor, then duck typing is used, ie. t and x are matched on a property by property basis.

Objects as Class instances: if t uses a constructor other than the default, then the x matches type t iff x and t use the same constructor.

Objects with Properties (duck typing, t has default object constructor)

t may specify required properties for x. t may also specify property variants, indicated by the variant prefix(usually '$'). A required property in x may match any of the corresponding property variants in t(with variant prefix removed). Property variants are used to create a type union. If x is missing a property specified in t, the check fails. However, x may contain additional properties that are not specified in t. Extra properties are allowed so that objects may be easily extended or enhanced.

For each property specified in t and present in x, including property variants, the type of the value in x must match the type specified by the value in t or some variant.

For a given property name, If t only specifies property variants(indicated by the variant prefix), then that property is optional, but if that property is present in x it must match the type one of the corresponding variants of t.

t may contain one required property and several optional properties with the same name(after optional property prefixes are removed). Having multiple properties in t with the same resolved name creates a type union of each of the specified type values, thus the corresponding property value in x only needs to match any 1 of those types specified in t.

Comparisons to other type systems

One inspiration for value-types is Haskell's records syntax

Examples

// const {T,RT} = require("value-types").nocheck // disable checks, for production
const {T,RT} = require("value-types")
function T(){}  // type documentation
function dot3(v1, v2){
    T(v1, [0,0,0]);
    T(v2, [0,0,0]);
    return v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2];
}
function scale3(s, v){
    T(s, 0);
    T(v, [0,0,0]);
    return [s*v[0], s*v[1], s*v[2]];
}
function norm3(v){
    T(v, [0,0,0]);
    return Math.sqrt(dot3(v,v));
}


// use T(x, t) as an alias for JSType.typecheck_throw(x, t) for concise syntax.
var T = JSType.typecheck_throw // throws error if typecheck fails
// var T = function(){} // uncomment this for production to optimize out typechecks.

// Capitalize type names.
var Contact = {
    name: '',
    surname: '',
    _phone_nos: [''],
    _email: ''
};

var joe = {
    name: 'joe',
    surname: 'cool',
    email: 'joe@cool.com' };

T(joe, Contact);

function addPhoneNo(contact, phone_no){
    T(contact, Contact);
    T(phone_no, "");
    
    if(!("phone_nos" in contact)){
        contact.phone_nos = []; }
    
    contact.phone_nos.push(phone_no);
}

addPhoneNo(joe, "555.555.5555");

Readme

Keywords

none

Package Sidebar

Install

npm i value-types

Weekly Downloads

0

Version

0.4.3

License

ISC

Unpacked Size

26.7 kB

Total Files

5

Last publish

Collaborators

  • derekmc_