Skip to content

Instantly share code, notes, and snippets.

@branneman
Last active June 28, 2022 15:06
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save branneman/d6e2be5f9168ac57731ee2bd8d96934c to your computer and use it in GitHub Desktop.
Save branneman/d6e2be5f9168ac57731ee2bd8d96934c to your computer and use it in GitHub Desktop.
JavaScript strict type checking functions
module.exports = factory
function factory() {
const exports = {
isDef,
isUndef,
isNull,
isBool,
isNum,
isInt,
isStr,
isArr,
isObj,
isPOJO,
isFunc,
isSymbol
}
/**
*/
function isDef(x) {
return !isUndef(x) && !isNull(x)
}
/**
* Does not match: `null`
*/
function isUndef(val) {
return typeof val === 'undefined'
}
/**
* Does not match: `undefined`
*/
function isNull(val) {
return val === null
}
/**
*/
function isBool(val) {
return typeof val === 'boolean'
}
/**
* Does not match: `NaN`, `Infinity`, `-Infinity`
*/
function isNum(val) {
return typeof val === 'number' && isFinite(val)
}
/**
* Does not match: `NaN`, `Infinity`, `-Infinity`
*/
function isInt(val) {
return typeof val === 'number' && isFinite(val) && Math.floor(val) === val
}
/**
*/
function isStr(val) {
return typeof val === 'string'
}
/**
*/
function isArr(val) {
return Array.isArray(val)
}
/**
* Does not match: arrays, functions, symbols
*/
function isObj(val) {
return val === Object(val) && !isArr(val) && !isFunc(val) && !isSymbol(val)
}
/**
*/
function isPOJO(x) {
return isObj(x) && Object.getPrototypeOf(x) === Object.prototype
}
/**
*/
function isFunc(val) {
return typeof val === 'function'
}
/**
*/
function isSymbol(val) {
return typeof val === 'symbol'
}
return exports
}
const factory = require('./type-checks')
const types = {
undef: [undefined, void 42],
nil: [null],
bool: [false, true, Boolean()],
int: [
0,
1,
0xbada55, // Base 16: hex
0o2471, // Base 8: octal,
Number.MAX_SAFE_INTEGER
],
num: [
1.01,
0.30000000000000004 // 0.1 + 0.2
],
specialNum: [NaN, Infinity, -Infinity],
str: ['', String(), String(''), String('abc'), 'latin', '日本語'],
arr: [
[],
[1, 'two'],
Array(3), // [ <3 empty items> ]
[null, undefined],
new (class A extends Array {})() // []
],
obj: [
new (class A {})(),
new (function Foo() {})(),
Object.prototype,
Object.create(null),
Object.create({}),
new Date(),
new RegExp()
],
pojo: [
{},
Object(),
new Object(),
Object.create(Object.prototype),
{ length: 0 }, // Not an array
{ length: 2, '0': 42 }, // Not an array
{ answer: 42 }
],
sym: [Symbol(), Symbol('label')],
fun: [
() => 0,
function() {},
function named() {},
Function(),
parseInt,
Symbol // Function
]
}
describe('type checks', () => {
it('isDef()', () => {
const { isDef } = factory()
const match = [].concat(
types.bool,
types.int,
types.num,
types.specialNum,
types.str,
types.arr,
types.obj,
types.pojo,
types.sym,
types.fun
)
const nomatch = [].concat(types.undef, types.nil)
match.forEach(val => {
if (!isDef(val)) throw val
})
nomatch.forEach(val => {
if (isDef(val)) throw val
})
})
it('isUndef()', () => {
const { isUndef } = factory()
const match = types.undef
const nomatch = [].concat(
types.nil,
types.bool,
types.int,
types.num,
types.specialNum,
types.str,
types.arr,
types.obj,
types.pojo,
types.sym,
types.fun
)
match.forEach(val => {
if (!isUndef(val)) throw val
})
nomatch.forEach(val => {
if (isUndef(val)) throw val
})
})
it('isNull()', () => {
const { isNull } = factory()
const match = types.nil
const nomatch = [].concat(
types.undef,
types.bool,
types.int,
types.num,
types.specialNum,
types.str,
types.arr,
types.obj,
types.pojo,
types.sym,
types.fun
)
match.forEach(val => {
if (!isNull(val)) throw val
})
nomatch.forEach(val => {
if (isNull(val)) throw val
})
})
it('isBool()', () => {
const { isBool } = factory()
const match = types.bool
const nomatch = [].concat(
types.undef,
types.nil,
types.int,
types.num,
types.specialNum,
types.str,
types.arr,
types.obj,
types.pojo,
types.sym,
types.fun
)
match.forEach(val => {
if (!isBool(val)) throw val
})
nomatch.forEach(val => {
if (isBool(val)) throw val
})
})
it('isNum()', () => {
const { isNum } = factory()
const match = [].concat(types.int, types.num)
const nomatch = [].concat(
types.undef,
types.nil,
types.bool,
types.specialNum,
types.str,
types.arr,
types.obj,
types.pojo,
types.sym,
types.fun
)
match.forEach(val => {
if (!isNum(val)) throw val
})
nomatch.forEach(val => {
if (isNum(val)) throw val
})
})
it('isInt()', () => {
const { isInt } = factory()
const match = [].concat(types.int)
const nomatch = [].concat(
types.undef,
types.nil,
types.bool,
types.num,
types.specialNum,
types.str,
types.arr,
types.obj,
types.pojo,
types.sym,
types.fun
)
match.forEach(val => {
if (!isInt(val)) throw val
})
nomatch.forEach(val => {
if (isInt(val)) throw val
})
})
it('isStr()', () => {
const { isStr } = factory()
const match = types.str
const nomatch = [].concat(
types.undef,
types.nil,
types.bool,
types.int,
types.num,
types.specialNum,
types.arr,
types.obj,
types.pojo,
types.sym,
types.fun
)
match.forEach(val => {
if (!isStr(val)) throw val
})
nomatch.forEach(val => {
if (isStr(val)) throw val
})
})
it('isArr()', () => {
const { isArr } = factory()
const match = types.arr
const nomatch = [].concat(
types.undef,
types.nil,
types.bool,
types.int,
types.num,
types.specialNum,
types.str,
types.obj,
types.pojo,
types.sym,
types.fun
)
match.forEach(val => {
if (!isArr(val)) throw val
})
nomatch.forEach(val => {
if (isArr(val)) throw val
})
})
it('isObj()', () => {
const { isObj } = factory()
const match = [].concat(types.obj, types.pojo)
const nomatch = [].concat(
types.undef,
types.nil,
types.bool,
types.int,
types.num,
types.specialNum,
types.str,
types.arr,
types.sym,
types.fun
)
match.forEach(val => {
if (!isObj(val)) throw val
})
nomatch.forEach(val => {
if (isObj(val)) throw val
})
})
it('isPOJO()', () => {
const { isPOJO } = factory()
const match = types.pojo
const nomatch = [].concat(
types.undef,
types.nil,
types.bool,
types.int,
types.num,
types.specialNum,
types.str,
types.arr,
types.obj,
types.sym,
types.fun
)
match.forEach(val => {
if (!isPOJO(val)) throw val
})
nomatch.forEach(val => {
if (isPOJO(val)) throw val
})
})
it('isFunc()', () => {
const { isFunc } = factory()
const match = types.fun
const nomatch = [].concat(
types.undef,
types.nil,
types.bool,
types.int,
types.num,
types.specialNum,
types.str,
types.arr,
types.obj,
types.pojo,
types.sym
)
match.forEach(val => {
if (!isFunc(val)) throw val
})
nomatch.forEach(val => {
if (isFunc(val)) throw val
})
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment