github

pwnn / is.js

  • суббота, 11 июня 2016 г. в 03:12:58
https://github.com/pwnn/is.js

JavaScript
Minimalistic predicate library.



is.js

TravisCI

Minimalistic predicate library.

Install

Node:

$ npm install --save @pwn/is

Browser:

<script src="path/to/is.min.js"></script>

Features

  • Zero dependencies.
  • Works with Node, AMD and all browsers, including IE6.
  • Modular and extensible.

Usage

A code sample is worth a thousand words:

const is = require( '@pwn/is' )

is.array( [] ) // true
is.not.integer( 0 ) // false
is.propertyDefined( { foo : { bar : 0 } } , 'foo.bar' ) // true
is.equal( [ 1 , [ 2 , 3 ] ] , [ 1 , [ 2 , 3 ] ] ) // false
is.deepEqual( [ 1 , [ 2 , 3 ] ] , [ 1 , [ 2 , 3 ] ] ) // true

// use a third-party bundle
is.use( require( 'path/to/some/math/bundle' ) )
is.prime( 7 ) // true

All checks, or predicates in is.js terminology, takes two general forms:

  • POSITIVE CHECK: is.predicate( ...args ) - Checks whether certain condition met.
  • NEGATIVE CHECK: is.not.predicate( ...args ) - The inverse of its corresponding positive check.

That's it! What's next?

Cheatsheet

TL;DR

A bundle is simply a way of organizing related predicates.

bundle:nil

bundle:number

bundle:string

bundle:boolean

bundle:object

bundle:array

bundle:type

bundle:equality

API reference

is.null( value )

Checks whether given value is null.

is.null( null ) // true
is.null( undefined ) // false

is.undefined( value )

Checks whether given value is undefined.

is.undefined( null ) // false
is.undefined( undefined ) // true

is.exist( value )

Checks whether given value exists, i.e, not null nor undefined.

is.exist( null ) // false
is.exist( undefined ) // false

is.nil( value )

Checks whether given value is either null or undefined.

is.nil( null ) // true
is.nil( undefined ) // true

is.number( value )

Checks whether given value is a number.

is.number( 0 ) // true
is.number( Number.NaN ) // true
is.number( Number.POSITIVE_INFINITY ) // true
is.number( Number.NEGATIVE_INFINITY ) // true
is.number( '0' ) // false
is.number( new Number( 0 ) ) // false

is.numeral( value )

Checks whether given value is a numeral, i.e:

  • a genuine finite number
  • or a string that represents a finite number
is.numeral( null ) // false
is.numeral( undefined ) // false
is.numeral( true ) // false
is.numeral( false ) // false
is.numeral( Symbol( 0 ) ) // false
is.numeral( Symbol.for( 0 ) ) // false
is.numeral( { valueOf() { return 0 } } ) // false
is.numeral( [ 0 ] ) // false
is.numeral( () => 0 ) // false

is.numeral( '' ) // false
is.numeral( 'one' ) // false
is.numeral( '1px' ) // false
is.numeral( '  0xFF  ' ) // true
is.numeral( '1e1' ) // true
is.numeral( '1.1E-1' ) // true
is.numeral( '-1' ) // true
is.numeral( '1.1' ) // true
is.numeral( new Number( 1 ) ) // true
is.numeral( new String( '-1.1' ) ) // true

is.numeral( Number.NaN ) // false
is.numeral( Number.POSITIVE_INFINITY ) // false
is.numeral( Number.NEGATIVE_INFINITY ) // false

is.nan( value )

Checks whether given value is NaN.

is.nan( 0 ) // false
is.nan( Number.NaN ) // true
is.nan( new Number( Number.NaN ) ) // false
is.nan( Number.POSITIVE_INFINITY ) // false
is.nan( Number.NEGATIVE_INFINITY ) // false
is.nan( 'one' ) // false

is.odd( number )

Checks whether given value is an odd number.

is.odd( 1 ) // true
is.odd( 2 ) // false
is.odd( '1' ) // false
is.odd( '2' ) // false
is.odd( new Number( 1 ) ) // false
is.odd( new Number( 2 ) ) // false
is.odd( Number.NaN ) // false
is.odd( Number.POSITIVE_INFINITY ) // false
is.odd( Number.NEGATIVE_INFINITY ) // false

is.even( number )

Checks whether given value is an even number.

is.even( 1 ) // false
is.even( 2 ) // true
is.even( '1' ) // false
is.even( '2' ) // false
is.even( new Number( 1 ) ) // false
is.even( new Number( 2 ) ) // false
is.even( Number.NaN ) // false
is.even( Number.POSITIVE_INFINITY ) // false
is.even( Number.NEGATIVE_INFINITY ) // false

is.finite( number )

Checks whether given value is a finite number.

is.finite( 0 ) // true
is.finite( '0' ) // false
is.finite( Number.NaN ) // false
is.finite( Number.POSITIVE_INFINITY ) // false
is.finite( Number.NEGATIVE_INFINITY ) // false

is.infinite( number )

Checks whether given value is an infinite number, i.e, Number.POSITIVE_INFINITY or Number.NEGATIVE_INFINITY.

is.infinite( 0 ) // false
is.infinite( '0' ) // false
is.infinite( Number.NaN ) // false
is.infinite( Number.POSITIVE_INFINITY ) // true
is.infinite( Number.NEGATIVE_INFINITY ) // true

is.integer( number )

Checks whether given value is an integer.

is.integer( 0 ) // true
is.integer( '0' ) // false
is.integer( new Number( 0 ) ) // false
is.integer( 0.1 ) // false
is.integer( Number.NaN ) // false
is.integer( Number.POSITIVE_INFINITY ) // false
is.integer( Number.NEGATIVE_INFINITY ) // false
is.integer( Number.MAX_SAFE_INTEGER ) // true
is.integer( Number.MIN_SAFE_INTEGER ) // true
is.integer( Number.MAX_SAFE_INTEGER + 1 ) // true
is.integer( Number.MIN_SAFE_INTEGER - 1 ) // true

is.safeInteger( number )

Checks whether given value is a safe integer.

is.safeInteger( 0 ) // true
is.safeInteger( '0' ) // false
is.safeInteger( new Number( 0 ) ) // false
is.safeInteger( 0.1 ) // false
is.safeInteger( Number.NaN ) // false
is.safeInteger( Number.POSITIVE_INFINITY ) // false
is.safeInteger( Number.NEGATIVE_INFINITY ) // false
is.safeInteger( Number.MAX_SAFE_INTEGER ) // true
is.safeInteger( Number.MIN_SAFE_INTEGER ) // true
is.safeInteger( Number.MAX_SAFE_INTEGER + 1 ) // false
is.safeInteger( Number.MIN_SAFE_INTEGER - 1 ) // false

is.string( value )

Checks whether given value is a string.

is.string( 'lipsum' ) // true
is.string( new String( 'lipsum' ) ) // false

is.emptyString( string )

Checks whether given value is an empty string, i.e, a string with whitespace characters only.

is.emptyString( '' ) // true
is.emptyString( ' ' ) // true
is.emptyString( '\f\n\r\t' ) // true
is.emptyString( '\u0009\u000A\u000B\u000C\u000D\u0020' ) // true
is.emptyString( 'lipsum' ) // false

is.substring( substring , string , [offset=0] )

Checks whether one string may be found within another string.

is.substring( 'ps' , 'lipsum' ) // true
is.substring( 'sp' , 'lipsum' ) // false
is.substring( [ 'ps' ] , 'lipsum' ) // true; `substring` will be converted to a string as needed
is.substring( 'ps' , [ 'lipsum' ] ) // false; `string` must be a string
is.substring( 'ps' , 'lipsum' , 2 ) // true
is.substring( 'ps' , 'lipsum' , 3 ) // false
is.substring( 'ps' , 'lipsum' , 3.14 ) // true; non-integer offset will be omitted and defaults to 0
is.substring( 'ps' , 'lipsum' , -4 ) // true; supports negative offset
is.substring( 'ps' , 'lipsum' , 6 ) // false; offset out of range
is.substring( 'ps' , 'lipsum' , -7 ) // false; offset out of range

is.prefix( prefix , string )

Checks whether string starts with prefix.

is.prefix( 'lip' , 'lipsum' ) // true
is.prefix( 'sum' , 'lipsum' ) // false
is.prefix( 'lip' , [ 'lipsum' ] ) // false; `string` must be a string
is.prefix( [ 'lip' ] , 'lipsum' ) // true - `prefix` will be converted to a string as needed

is.suffix( suffix , string )

Checks whether string ends with suffix.

is.suffix( 'sum' , 'lipsum' ) // true
is.suffix( 'lip' , 'lipsum' ) // false
is.suffix( 'sum' , [ 'lipsum' ] ) // false; `string` must be a string
is.suffix( [ 'sum' ] , 'lipsum' ) // true - `suffix` will be converted to a string as needed

is.boolean( value )

Checks whether given value is a boolean.

is.boolean( 1 ) // false
is.boolean( 0 ) // false
is.boolean( true ) // true
is.boolean( false ) // true
is.boolean( new Boolean( true ) ) // false
is.boolean( new Boolean( false ) ) // false

is.object( value )

Checks whether given value is an object.

is.object( null ) // false
is.object( undefined ) // false
is.object( 0 ) // false
is.object( new Number( 0 ) ) // true
is.object( '' ) // false
is.object( new String( '' ) ) // true
is.object( true ) // false
is.object( new Boolean( true ) ) // true
is.object( Symbol() ) // false
is.object( Symbol.for( 'is' ) ) // false
is.object( {} ) // true
is.object( [] ) // true
is.object( function () {} ) // true

is.emptyObject( object )

Checks whether given value is an empty object, i.e, an object without any own, enumerable, string keyed properties.

is.emptyObject( {} ) // true
is.emptyObject( { foo : 'bar' } ) // false
is.emptyObject( Object.create( { foo : 'bar' } ) ) // true; ignore inherited properties
is.emptyObject( Object.defineProperty( {} , 'foo' , { value : 'bar' } ) ) // true; ignore non-enumerable properties
is.emptyObject( { [ Symbol() ] : 0 } ) // true; ignore non-string-keyed properties

is.propertyDefined( object , path )

Checks whether path is a direct or inherited property of object.

is.propertyDefined( Object.create( { foo : 'bar' } ) , 'foo' ) // true
is.propertyDefined( { foo : { bar : { baz : 0 } } } , 'foo' ) // true
is.propertyDefined( { foo : { bar : { baz : 0 } } } , 'foo.bar' ) // true
is.propertyDefined( { foo : { bar : { baz : 0 } } } , 'foo.bar.baz' ) // true
is.propertyDefined( { foo : { bar : { baz : 0 } } } , 'foo.qux.baz' ) // false
is.propertyDefined( { foo : { bar : { baz : 0 } } } , 'foo.bar.baz.qux' ) // false

is.conforms( object , schema , [strict=false] )

Checks whether object conforms to schema.

A schema is an object whose properties are functions that takes these parameters(in order):

  • value:any - The value of current iteration.
  • key:string - The corresponding key of current iteration.
  • context:object - The object in question.

These functions, or validators, are called for each corresponding key in object to check whether object conforms to the schema. An object is said to be conforms to the schema if all validators passed.

In strict mode(where strict=true), is.conforms also checks whether object and schema has the same set of own, enumerable, string-keyed properties, in addition to check whether all validators passed.

is.conforms(
  { name : '@pwn/is' , access : 'public' } ,
  { name : is.exist }
) // true

is.conforms(
  { name : '@pwn/is' , access : 'public' } ,
  { description : is.string }
) // false; key `description` does not exist on `object`

is.conforms(
  { name : '@pwn/is' , access : 'public' } ,
  {
    name( value , key , context ) {
      return is.exist( value ) && context.access === 'public'
    }
  }
) // true


//
// strict mode
//

is.conforms(
  {
    name : '@pwn/is' ,
    access : 'public'
  } ,
  {
    name( value , key , context ) {
      return is.string( value ) && value.length >= 3
    }
  } ,
  true // enable strict mode
) // false; `object` has extraneous properties

is.array( value )

Checks whether given value is an array.

is.array( [] ) // true
is.array( '' ) // false
is.array( document.scripts ) // false
is.array( function() {} ) // false

is.arrayLikeObject( value )

Checks whether given value is an array-like object.

An object is qualified as array-like if it has a property named length that is a positive safe integer. As a special case, functions are never qualified as array-like.

is.arrayLikeObject( [] ) // true
is.arrayLikeObject( '' ) // false
is.arrayLikeObject( document.scripts ) // true
is.arrayLikeObject( function() {} ) // false

is.inArray( value , array , [offset=0] , [comparator=is.equal] )

Checks whether given array or array-like object contains certain element.

  • value: The element to search.
  • array: The array or array-like object to search from.
  • offset: The index to search from, inclusive.
  • comparator: The comparator invoked per element against value.
is.inArray( 2 , [ 1 , 2 , 3 ] ) // true
is.inArray( 4 , [ 1 , 2 , 3 ] ) // false
is.inArray( 2 , [ 1 , 2 , 3 ] , 1 ) // true
is.inArray( 2 , [ 1 , 2 , 3 ] , 2 ) // false
is.inArray( 2 , [ 1 , 2 , 3 ] , -2 ) // true; supports negative offset
is.inArray( 2 , [ 1 , 2 , 3 ] , 3 ) // false; offset out of range
is.inArray( 2 , [ 1 , 2 , 3 ] , -4 ) // false; offset out of range
is.inArray( [ 2 ] , [ 1 , [ 2 ] , 3 ] ) // false; default comparator is `is.equal`
is.inArray( [ 2 ] , [ 1 , [ 2 ] , 3 ] , 0 , is.deepEqual ) // true
is.inArray( [ 2 ] , [ 1 , [ 2 ] , 3 ] , is.deepEqual ) // true; `offset` can be omitted when passing a custom comparator only
is.inArray( 2 , [ 1 , 2 , 3 ] , ( val , arrMember ) => val === arrMember ) // true; `comparator` takes two parameters, the element to search and the array element of current iteration

is.sameType( value , other )

Checks whether given values are of the same type.

is.sameType( 0 , 0 ) // true
is.sameType( 0 , '0' ) // false
is.sameType( 0 , new Number( 0 ) ) // false
is.sameType( 0 , Number.NaN ) // true
is.sameType( [] , {} ) // false

is.primitive( value )

Checks whether given value is a primitive.

is.primitive( null ) // true
is.primitive( undefined ) // true
is.primitive( 0 ) // true
is.primitive( new Number( 0 ) ) // false
is.primitive( '' ) // true
is.primitive( new String( '' ) ) // false
is.primitive( true ) // true
is.primitive( new Boolean( true ) ) // false
is.primitive( Symbol() ) // true
is.primitive( Symbol.for( 'is' ) ) // true
is.primitive( {} ) // false
is.primitive( [] ) // false
is.primitive( function() {} ) // false

is.date( value )

Checks whether given value is a Date object.

is.date( new Date() ) // true

is.error( value )

Checks whether given value is an Error object.

is.error( new Error() ) // true
is.error( new TypeError() ) // true

is.function( value )

Checks whether given value is a function.

is.function( function () {} ) // true
is.function( () => null ) // true
is.function( new Function() ) // true

is.map( value )

Checks whether given value is a Map object.

is.map( new Map() ) // true

is.regexp( value )

Checks whether given value is a RegExp object.

is.regexp( /^/ ) // true
is.regexp( new RegExp() ) // true

is.set( value )

Checks whether given value is a Set object.

is.set( new Set() ) // true

is.symbol( value )

Checks whether given value is a symbol.

is.symbol( Symbol() ) // true
is.symbol( Symbol.for( 'is' ) ) // true

is.equal( value , other )

Checks whether given values are equal, using SameValueZero algorithm.

is.equal( null , undefined ) // false
is.equal( 0 , 0 ) // true
is.equal( 0 , '0' ) // false
is.equal( +0 , -0 ) // true; SameValueZero
is.equal( Number.NaN , Number.NaN ) // true; SameValueZero
is.equal( [] , [] ) // false

is.deepEqual( value , other )

Checks whether given values are deeply equal, i.e:

  • If Type( value ) !== Type( other ), returns false.
  • For primitives, checks whether they are equal using SameValueZero.
  • For arrays, checks whether they have same set of members, all of which are deeply equal.
  • Otherwise, checks whether they have same set of own, enumerable, string keyed properties, all of which are deeply equal.
is.deepEqual( null , undefined ) // false
is.deepEqual( 0 , 0 ) // true
is.deepEqual( 0 , '0' ) // false
is.deepEqual( +0 , -0 ) // true; SameValueZero
is.deepEqual( Number.NaN , Number.NaN ) // true; SameValueZero
is.deepEqual( [ 1 , { foo : [ 2 , [ 3 , 4 ] ] , bar : { baz : 5 } } ] , [ 1 , { foo : [ 2 , [ 3 , 4 ] ] , bar : { baz : 5 } } ] ) // true
is.deepEqual( Object.create( { foo : 1 } ) , Object.create( { foo : 2 } ) ) // true; only own, enumerable, string-keyed properties are checked

Writing new predicates

Predicates are essentially functions that checks whether certain condition met based on passed in arguments. They are packaged in various bundles. Conceptually, a bundle is simply a way of organizing related predicates. Implementation-wise, a bundle is a just a function that takes two parameters:

  • util:object - The utility object.
  • is:object - The is export.

The util object defines a method called addPredicate that allows you to define new predicates:

util.addPredicate( name:string , predicate:function )

  • name - The name of the predicate.
  • predicate - The predicate function.

Once defined, the predicate will be available on both is and is.notutil.addPredicate wraps the predicate in a delegate function and automatically handles positive/negative cases for you.

Still confused? Take a look at this sample bundle:

// my_bundle.js

// `util` and `is` are passed in as free variables, so you don't
// need to call `require( '@pwn/is' )`
module.exports = function bundle( util , is ) {

  util.addPredicate( 'positive' , function isPositive( value ) {
    return is.number( value ) && value > 0
  } )

  util.addPredicate( 'negative' , function isNegative( value ) {
    return is.number( value ) && value < 0
  } )

}

To use a bundle, simple call is.use:

is.use( bundle:function )

const is = require( '@pwn/is' )

// import all predicates from my_bundle.js
is.use( require( 'path/to/my_bundle' ) )

is.positive( +1 ) // true
is.not.positive( -1 ) // true
is.negative( -1 ) // true
is.not.negative( +1 ) // true