types.js
A tiny and fast dynamic type checker/enforcer library.
Why
Because only with dynamic type checking I can be 100% sure my app handles type issues properly at run time.
Performance
The performance penalty is negligible for the majority of operations. It's roughly between 0.00002ms and 0.0001ms per call (on my 4yr old laptop).
Features
- introduces new types and a typeof replacement for proper type assessment
- fixes NaN, array, null, etc..
- can check one or multiple values at once
- 4 convenience forms: is[type], not[type], has[type] and all[type]
- force any return value to be of any specific type
- create immutable 'enum' objects
- battle tested
Some examples
const { enumerate, typeOf, forceString, forceNumber, intoArray, allDefined,
hasObject } = require( 'types.js' );
typeOf( [] );
typeOf( null );
typeOf( /someregexp/ );
typeOf( parseInt('Not A Number!') );
forceString( 12 );
forceNumber( '12px' );
forceNumber( 'will use next arg..', 12 );
intoArray( '1 2 3' );
intoArray( '1', '2', '3' );
allDefined( 'good', false, 0 );
hasObject( 'not', 'really' );
const COLORS= enumerate( ['RED', 'GREEN', 'BLUE'] );
console.log( COLORS.BLUE );
Force!
Force a value to be of a specific type. A replacement value can be given (as second argument) for in case the first provided value is incompatible. If none of the given values is compatible a literal of that type is returned (except for Number).
For example a look at how we can safely call a function that needs to pass a number argument, first in
"standard" JS, next with types.js force methods:
function test( left, callback ){
if ( typeof left !== 'number' ){
left= parseInt( left, 10 );
}
if ( left !== left || typeof left !== 'number' ){
left= 0;
}
if ( typeof callback === 'function' ){
callback( left );
}
left= forceNumber( left, 0 );
forceFunction( callback )( left );
}
test( '20px', console.log );
Check it out, it's sweet! I've added force to types.js because I use it all the time and it seems to belong in here.
node.js
For use with node.js you can install with npm install types.js
AMD
require.config({
paths: {
'types', [ 'path/to/types.min(.js)' ]
}
});
require( ['types'], function( _ ){
console.log( types.isNumber(0) );
});
Basic usage:
force[Type] Forces a value to be of a given type, and returns that value, a given replacement, or a literal for that Type
is[Type] and not[Type] are useful for single argument type checking.
all[Type] is useful for checking if all given arguments are of a certain type.
has[Type] is useful for checking if one or more arguments are of a certain type.
typeof Returns a lowercase string representation of the type of the argument value, according to types.js type-definitions.
intoArray Converts arguments or space delimited strings into an array.
enumerate Creates an immutable 'enum' object
some more examples:
var types= Types;
var types= require( 'types.js' );
var x;
types.forceString( x );
types.forceString( null, 'ok' );
types.forceString( null, [1, 2, 3] );
types.forceString(33, 'not used');
types.forceNumber('35px');
types.forceString(33, 'must be string');
types.forceNumber( true, 0 );
types.forceBoolean('35px');
types.forceArray("you'll get an array!");
types.intoArray( 'hi', 'there' );
types.intoArray( ' hi there ' );
types.intoArray( '', 0, {}, [] );
var func= null;
types.forceFunction( func )( 'arguments for func' );
types.isDefined()
types.isString( 'Hello types.js!' );
types.isString( 23456 );
types.isBoolean( false );
types.isArray( [1,2,3] );
types.isObject( [1,2,3] );
types.isObject( /myRegExp/g );
types.isNaN( parseInt('generate NaN') );
types.notNull('');
types.notUndefined( undefined );
types.isDefined( null );
types.allString( '', " ", 'with text' );
types.allString( '', ' ', 'with text', 123 );
types.allStringOrNumber( '', ' ', 'with text', 123 );
types.allObject( { key: 'nice' }, [], /regexp/ig );
types.allArray( [1,2,3], [{}], new RegExp('stop') );
types.allArray( [1,2,3], [{}], [false, true] );
types.hasString( 123, { value: 'nice' }, ['?'] );
types.hasStringOrNumber( [1,2], /reg/, 'true' )
types.hasFunction( 123, { value: 'nice' }, function(){} );
types.hasUndefined( 'render false!', 123, null );
types.hasUndefined( 'render true!', 123, undefined );
types.typeof( [1,2,3] );
types.typeof( null );
types.typeof( parseInt('generate NaN') );
types.typeof( new Date() );
API
Types.parseIntBase
<Number> parseIntBase= 10
Holds the Radix used by forceNumber, defaults to 10 (native default). Can be set to valid radixes for parseInt(). Use with extreme care because once set, all following forceNumber calls will use the new Radix!
types.parseIntBase= 0x10;
var nr= types.forceNumber( 'a' );
console.log( nr );
Types.force[Type]
<[type]> force[Type]( <[type]> value, <[type]> replacement )
Force the return value to be of a specific type. A replacement return value can be given for in case value is incompatible. The replacement value should of course be of the same type, otherwise it will be rejected as well. If no proper value for the given type is found a literal of that type will be returned (with an exception of Number(see below)).
All force[types] share the same methodology, only applying the type denoted by the method name. See the following examples
for it's workings.
Types.forceBoolean
<Boolean> Types.forceBoolean( value, replacement )
Returns value if value is a types.js boolean. Otherwise it will try to convert value to be true or false. If that fails too,
replacement will be tested for, or converted to boolean if possible. If that fails, the default types.js boolean literal
is returned: a Boolean false
var assert= types.forceBoolean( 'Only a true true returns true' );
console.log( assert );
var assert= types.forceBoolean( NaN != NaN );
console.log( assert );
Types.forceString, Types.forceArray, Types.forceObject, Types.forceRegExp
Same as .forceBoolean, except for the type being processed.
Types.forceNumber
<Number> forceNumber( <String>/<Number> value, <String>/<Number> replacement )
Returns value if it is a types.js number or convertable to a number. Returns replacement if value is invalid or not convertable.
Returns a native Number object with a .void property set to true if no valid value and replacement were given and conversion
was not possible.
You can check value.void to see if value is ready to be fetched. If value.void is true, you can only fetch it's
default value by doing some mathematical operation on it like: value+ 0
, otherwise you'll get an object. After
any mathematical operation on value, value.void will be undefined and value has become a 'real' number.
If value.void is true, the default value returned with value+ 0
is 0.
If you want to be sure that forceNumber returns a 'real' number, then simply supply a 'real' number replacement,
like: var nr= types.forceNumber(nr, 0);
, and it will never return the Number-object form.
Types.typeof( nr= Types.forceNumber() );
returns 'number', also if nr.void === true
Example: make a numberFilter for arguments with forceNumber:
function numberFilter(){
var numbers= [];
for( var arg in arguments ){
var value= types.forceNumber( arguments[arg] );
if( value.void )
continue;
numbers.push( value );
}
return numbers;
}
function forceArgsToNumber(){
return numberFilter.apply( this, arguments );
}
console.log( forceArgsToNumber('ignore', 1, 'the', 2, 'strings!', 3) );
console.log( forceArgsToNumber('1 but', '2 not', '3 unconditional!') );
Types.forceFunction
<Function> Types.forceFunction( <Function> func, <Function> replacement )
Returns func if it is a Function. forceFunction will not try/catch func for other failures.
If func or replacement are not a Function, a dummy function(){} will be returned. So you can safely call your function with
Types.forceFunction(func)( args )
. If it is a Function, it will call func and pass the given arguments.
var showAuthor= function( name ){
console.log( 'Author: '+ types.forceString(name) );
};
var brokenFunc= null;
var func= types.forceFunction( showAuthor );
console.log( func === showAuthor );
var func= types.forceFunction( brokenFunc, showAuthor );
console.log( func === showAuthor );
var func= types.forceFunction( brokenFunc, brokenFunc );
func();
types.forceFunction( brokenFunc, showAuthor )( 'Dennis' );
types.forceFunction( brokenFunc, brokenFunc )( 'Dennis' );
Types.logForce
<undefined> Types.logForce( <Function> handler )
You can call Types.logForce(); to turn on console logging for failed type checks of force[type]. If you prefer a more advanced handler than the basic provided console.log, you can simply pass that handler as argument. The handler will receive 3 arguments: errLevel, expectedType, encounteredType.
types.logForce();
var result= types.forceString( [], 'ok' );
console.log( result );
Types.intoArray
<array> Types.intoArray( <[type]> arg1, ..., argN )
intoArray is a convenience method I use mostly for flexible arguments passing, but can also be used to split
space delimited strings into an array in a non-sparse way (destructing spaces).
It accepts 3 kinds of arguments:
kind | input | result |
---|
space delimited strings | 'This is intoArray!' | ['This', 'is', 'intoArray!'] |
multiple arguments | 'This', 'is', 'intoArray!' | ['This', 'is', 'intoArray!'] |
array | ['This', 'is', 'intoArray!'] | ['This', 'is', 'intoArray!'] |
All generating the same forced array.
function testArgs( arg1, ..., argN ){
var array= types.intoArray.apply( this, arguments );
console.log( array );
}
testArgs( 'This', 'is', 'intoArray!' );
testArgs( ['This', 'is', 'intoArray!'] );
testArgs( 'This is intoArray!' );
For space delimited string parsing, all strings are trimmed and reduced to one consecutive space
to avoid a sparse array.
Types.typeof and Types.typeOf
<String> Types.typeOf( value )
Returns a lowercase string representation of the type of value, according to types.js types. See all types.js
type-definitions below.
var number= parseInt( 'damn NaN!' );
console.log( types.typeOf(number) );
Types.isBoolean
<Boolean> Types.isBoolean( value )
Returns true if the given argument is a Boolean true or false
console.log( types.isBoolean(false) );
Types.notBoolean
<Boolean> Types.isBoolean( value )
Returns true if the given argument is not a Boolean true or false
console.log( types.notBoolean('not a Boolean') );
Types.hasBoolean
<Boolean> Types.hasBoolean( values, [value1, ..., valueN])
Returns true if any of the given arguments is a Boolean true or false
console.log( types.hasBoolean('the third', null, false) );
Types.allBoolean
<Boolean> Types.allBoolean( values, [value1, ..., valueN])
Returns true only if all given arguments are either a Boolean true or false
console.log( types.allBoolean(false, null, true) );
Types.getFirst[type]
<[type]> Types.getFirst[type]( [<[any type]>value1, ..., <[any type]>valueN] )
Returns the first found argument of a specific type.
console.log( types.getFirstString(false, 'hello', true) );
Types.enum
<Object> Types.enum( [<String>item1, ..., <String>itemN], <Number>offset )
Returns an object with the given items as keys with their respective indices as values. If an offset is given, the index count starts form the value of the offset.
Please note that types.typeof(myEnum) will return 'object', as it actually is an object. Nevertheless we can test with isEnum, hasEnum, etc.. for it to be of the 'virtual' type 'enum'.
var COLORS= types.enum( ['RED', 'BLUE', 'GREEN'] );
console.log( COLORS.BLUE );
console.log( isEnum(COLORS) );
console.log Types.getFirstEnum 1, COLORS
COLORS= types.enum( ['RED', 'BLUE', 'GREEN'], 10 );
console.log( COLORS.BLUE );
Types.disableLogging
<undefined> Types.disableLogging()
Disables all logging
Types.enableLogging
<undefined> Types.enableLogging()
Re-enables logging
not / is / has / all'Types'
All remaining methods are equal to the last four above, except for that they differ in the type being checked. The complete
list of all these methods:
not | is | has | all |
---|
notBoolean | isBoolean | hasBoolean | allBoolean |
notString | isString | hasString | allString |
notNumber | isNumber | hasNumber | allNumber |
notStringOrNumber | isStringOrNumber | hasStringOrNumber | allStringOrNumber |
notObject | isObject | hasObject | allObject |
notArray | isArray | hasArray | allArray |
notFunction | isFunction | hasFunction | allFunction |
notRegexp | isRegexp | hasRegexp | allRegexp |
notDate | isDate | hasDate | allDate |
notNull | isNull | hasNull | allNull |
notSymbol | isSymbol | hasSymbol | allSymbol |
notUndefined | isUndefined | hasUndefined | allUndefined |
notDefined | isDefined | hasDefined | allDefined |
notNaN | isNaN | hasNaN | allNaN |
notEnum | isEnum | hasEnum | allEnum |
types.js type definitions:
All types.js types
type | definition |
---|
'undefined' | Any value that is undefined, or no value at all |
'null' | Any value that is null |
'boolean' | A boolean true or false, or the result of a predicate |
'string' | A string literal |
'number' | A number literal or a 'void' Number |
'nan' | Any value that is NaN |
'object' | An object literal {}, or any instance of Object that is not a native JS object (except for Object) |
'array' | An array literal [], or any instance of Array |
'function' | Any function or instance of Function |
'regexp' | Any regular expression literal, or any instance of RegExp |
'date' | Any instance of Date |
'symbol' | Any Symbol created with Symbol() |
force[Type] default return values
type | return value |
---|
forceBoolean | false |
forceString | '' |
forceNumber | a new Number with a .void property set to true |
forceObject | {} |
forceArray | [] |
forceFunction | function(){} |
forceRegExp | /(?:)/ |
change log
1.9.0
- adds babel transpiler as dev dependency for better cross browser compatibility
1.8.0
- adds 'symbol' type to types.js
- is/not/has/allSymbol now available
- adds tests for type 'symbol'
- fixes a minor flaw in autoConvert for forceNumber, it now only attempts to auto-convert values of type 'string'
1.7.0
- adds types.enum and types.enumerate, for creating enumerables
- adds types.autoConvert (default == true), to allow for disabling automatic String <=> Number conversion with forceString and forceNumber
- adds types.typeOf as alias for types.typeof
- adds types.disableLogging() and types.enableLogging()
- logForce now sends errLevel, expectedType and encounteredType to a custom log handler
- some refactoring
- removes old html style jasmine import for tests
- adds tests for types.enum
- adds tests for types.autoConvert
- updates README.md including usage examples for enum
1.6.1
1.6.0
- adds forceRegExp
- adds logForce, now we can show a log when forceTypes encounters a type mismatch
- updates readme
1.5.3
adds getFirst, returning the first found argument of a specific type
1.5.1
Some minor changes to this readme
1.5.0
Added .intoArray method.
1.4.4
Added AMD support.
1.4.2
Optimized and reworked the codebase, and some adjustments to tests.
Updated the readme.
1.3.9
Removed 'unknown' from types.js type definitions. It was meant to be like a final state, for if no other matching type could
be found, but in the codebase as it is now, that state can never be reached.. If Javascript ever invents a brand new type,
types.js will return 'defined' on that one if I would not take action and implement support for it.
Updated the readme.
1.3.5
Changed:
-
forceNumber doesn't return 0 by default anymore. It now returns a Number object with a .void property which is set to
true if no valid Number was given or no conversion was possible.
Just use: types.forceNumber( value, 0 );
to return a 0 as replacement, it only is not default anymore.
I made this change because I wanted to be able to check if forceNumber was successful. Just a 0 can be very misleading and
a source for bugs. NaN is a failure IMO, so I made a kind of replacement feature in forceNumber.
You can now check for yourNumber.void to see if it is set. If .void is true, yourNumber is a Number object which is ready for
mathemetical operation, and defaults to 0, this in contrast with NaN, which is almost totally unusable.
example:
var nr= forceNumber();
console.log( nr.void );
console.log( nr );
console.log( 0 + nr );
( nr.void )
? console.log( 'void?', nr+= 36/ 4 );
: console.log( nr );
etc..
Updated:
- Jasmine tests for forceNumber and isDefined
- speed optimization for isObject
1.3.1
Added:
-
change log in the readme, more convenient overview of changes.
-
is/not/has/allDefined
Now you can: if (types.isDefined(value) )
instead of if (types.notUndefined(value) )
###license
MIT