Not is a minimal, blazing fast, intuitive, and customisable type-checking helper. Meet project deadlines. Code with accuracy. No compiling required. Not checks at runtime, useful to complement Typescript.
This module has no dependencies.
Not uses a double negative mechanism (it is more powerful and will be explain further below). Simplest usage looks like this:
const Not = require('you-are-not')
let not = Not.create()
let str = 'string'
not('number', str) // throws error "Wrong Type: Expecting `number` but got `string`."
//or check objects
let anotherNot = Object.create(Not)
let schema = {
{ string: 'string', number: 'number' } // a schema that replicates intuitively your object structure
}
let candidateToCheck = { string: 123, number: '123' } // wrong typing.
let errors = anotherNot.checkObject(
'objectName',
schema,
candidateToCheck
)
// throws error, errors are organised neatly into an array.
Not is that small and convenient type-checking validation library you have been missing to do just that. It also overcomes JS quirks that gets in the way, like: typeof null // 'object'.
Typescript doesn't check at runtime, you need to complete it with Not. Written in full JS and assailed with tests, it doesn't add to your compilation time.
Unlock flexibility of Javascript, where typing need not be strict, and functions/APIs (especially client-facing ones) are made powerful by being able to accept different argument types, or error gracefully.
Write good code quickly; find the balance in code accuracy and writing speed. Leverage flexibility that Javascript has intended for.
npm i --save you-are-not
Not prefers a more powerful "double negative" mechanism, to definitively return false when the check passes. It follows the sensible human logic -- "Let's check if something is wrong (not what I want), so that I will do something. Nope, lets move on":
if (not('string', 'i am string')) // do something
// Nope, let's move on.
When Not fails, it throws an error by default. Or, you can cleverly return a string which can be used to evaluate to true to perform some operations!
const not = Not.create({ willThrowError: false })
// instead of throwing, `not` will return string
let input = ['a', 'sentence']
let result = not('string', input) // returns a string, which can evaluate `true`
if (result) input = input.join(' ')
// so you can do your own error handling, or transformation
// code below can safely use `input` as string :)
input.toLowerCase()
const Not = require('you-are-not')
function test(foo, bar) {
let not = Not.create()
// usage (accepts 4 arguments):
not(
'string',
// type to expect
//can be STRING or an ARRAY OF STRINGS
foo,
// the candidate being checked,
'FOO',
// (optional) name of the candidate
// to prepare error message
'[MESSAGE or TIMESTAMP]'
// (optional) any additional notes
// will be added to the message.
)
not(['undefined', 'number'], bar, 'bar')
// this means `bar` can optional,
// if not must be a number.
}
let fooNotString = ['foo']
test(fooNotString)
// will throw: ! Wrong Type (FOO): Expect type `string` but got `array`. [MESSAGE or TIMESTAMP].
#checkObjectIn the real world, the our API params checking is a ~~leaning~~ tower of code. Not makes it neat and produces super user-friendly error messages. No one loses hair:
const Not = require('you-are-not')
someAPIEndPoint((request, response) => {
/* checking starts */
let apiNot = Object.create(Not) // use the full object, don't call #create.
apiNot.willThrowError = false
/* Usage
#checkObject(
name of object,
expectations <object>,
what you got <object>,
callback <function> [optional]
)
or
#checkObject(
name of object,
expectations <object>,
what you got <object>,
{
callback <function> [optional],
returnPayload <boolean> [optional]
}
)
*/
let errors = apiNot.checkObject('request', {
name: 'string',
subscribe: 'boolean',
"info?": { // not that for objects that are optional, use "__optional" or suffix "?".
gender: 'string',
age: ['string', 'optional'] // it can be optional, or if present, it must be string.
}
}, request.body, callback)
// provide `callback` if you wish to handle the error yourself.
if (errors) return response.status(500).send({ error })
response.status(200).send('Success!')
})
Not can sanitise your payload:
let schema = {
valid: 'string',
toSanitise: {
keepThis: 'array'
}
}
let payload = {
valid: 'abc',
toSanitise: {
keepThis: [],
omitThis: 123
}
}
let sanitised = apiNot.checkObject(
'request',
schema,
payload, {
callback: function(errors, payload) { ... },
returnPayload: true
}
})
// if is array, means something failed.
if(Array.isArray(sanitised)) throw sanitised
doSomethingWithYourPayload(sanitised)
You can also use #lodge And #resolve to bulk checking with more control:
apiNot.lodge('string', request.name, 'name')
apiNot.lodge('boolean', request.subscribe, 'subscribe')
apiNot.lodge(['string', 'array'], request.friends, 'friends')
apiNot.lodge(['number', 'string'], request.age, 'age')
// and many more lines
apiNot.resolve()
/* OR */
apiNot.resolve(errors => {
// optional callback, custom handling
throw errors
})
Primitives:
'string'
'number'
'nan' // this is an opinion. NaN should not be of type number in the literal sense.
'array'
'object'
'function'
'boolean'
'null'
'undefined'
Aggregated:
'optional' // which means 'null' and 'undefined'
#islet is = Not.createIs()
is('array', []) // returns true
is('number', NaN) // returns false
Because #is needs to return true when the check passes, it is not as powerful as #not.
Instead of the horrible:
if (
typeof foo !== 'string'
|| (typeof foo !== 'number'
|| (typeof foo === 'number' && !isNaN(chk)))
|| !Array.isArray(chk)
) {
throw Error("Not valid, but I don't know why.")
}
You write:
not(['string', 'number', 'array'], foo)
// or
is(['string', 'number', 'array'], foo)
let not = Object.create(Not)
not.defineType({
primitive: 'number', // you must define your primitives
type: 'integer', // name your test
pass: function(candidate) {
return candidate.toFixed(0) === candidate.toString()
}
})
not.not('integer', 4.4) // gives error message
not.is('integer', 4.4) // returns false
not.defineType({
primitive: ['null', 'undefined', 'boolean', 'object', 'nan', 'array' ],
type: 'falsey',
pass: function(candidate) {
if (not.is('object', candidate)) return Object.keys(candidate).length === 0
if (not.is('array', candidate)) return candidate.length === 0
if (not.is('boolean', candidate)) return candidate === false
// its the other primitives null, undefined and nan
// which is to be passed as falsey straight away without checking
return true
}
})
not.not('falsey', {}) // returns false
not.not('falsey', [null]) // returns error message
not.is('falsey', []) // returns true
not.is('falsey', undefined) // returns true
not.is(['falsey', 'function'], function() {}) // returns true
typeof [] // object
typeof null // object
typeof NaN // number
Those are technically not wrong (or debatable), but often gets in the way.
NaN is not a 'number', and will be 'nan'.Array and [] are of 'array' type, and not 'object'.null is 'null' and not an 'object'.#String is 'string' and not an 'object'.You can switch off opinionated type-checking:
let not = Not.create({ isOpinionated: false })
When false, all Javascript the quirks will be restored, on top of Not's opinions: An Array will both be an 'array' as well as 'object', and null will both be 'null' and 'object':
not('object', []) // returns false -- `[]` is an object
not('array', []) // returns false -- `[]` is an array
not('object', null) // returns false -- `null` is an object
// both #createIs and #create can take in the same options
let NotWithPartialOpinions = Not.createIs({
opinionatedOnNaN: false
opinionatedOnArray: false
opinionatedOnNull: false
opinionatedOnString: false
})
// or mutate the object before instantiating.
let NotWithPartialOpinions = Object.create(Not)
Object.assign(NotWithPartialOptions, {
opinionatedOnNaN: false
opinionatedOnArray: false
opinionatedOnNull: false
opinionatedOnString: false
})
let not = NotWithPartialOpinions.create()
let is = NotWithPartialOpinions.createIs()
You have to mutate the prototype:
const CustomNot = require('you-are-not')
//overwrite the msg function with your own
CustomNot.msg = function(expect, got, name, note) {
let msg = 'Hey there! We are sorry that something broke, please try again!'
let hint = ` [Hint: (${name}) expect ${expect} got ${got} at ${note}.]`
return global.isDeveloperMode ? msg += hint : msg
}
let customNot = CustomNot.create()
global.isDeveloperMode = true
customNot('string', [], 'someWrongInput', 'file.js - xxx function')
Will throw:
! Error: Hey there! We are sorry that something broke, please try again! [Hint: (someWrongInput) expect string got array at file.js - xx function. ]
Not is MIT licensed.