takor

Comprehensive and flexible runtime type assertion

Usage no npm install needed!

<script type="module">
  import takor from 'https://cdn.skypack.dev/takor';
</script>

README

Flexible and composable runtime type assertion for Javascript. Syntax inspired by prop-types. Supports:

  • ES6 data structures
  • inner elements in data structures
  • nested assertions
  • inverse assertions
  • custom validators

Quick Examples

// takor.oneOf
const isNumOrStr = takor.oneOf(Number, String)
isNumOrStr(10) // true
isNumOrStr(new Set) // false

// takor.shape basic
const checkShape = takor.shape({
    key1: takor.oneOf(Number, String)
})
checkShape({ // true
    key1: 'string'
})

// takor.shape with nested assertions
const checkNestedElement = takor.shape({
    key1: takor.shape({
        key2: takor.arrayOf(Number, Set, String)
    })
})
checkNestedElement({ // true
    key1: {
        key2: [0, new Set, '']
    }
})

checkNestedElement({ // false
    key2: {
        key1: [0, new Set, '']
    }
})

// supports literal number or string
const isValidDogBreed = takor.oneOf('terrier', 'greyhound', 'golden retriever')
isValidDogBreed('terrier') // true
isValidDogBreed('persian') // false

// custom validator(s)
const lessThanTen = (el) => el < 10
const greaterThanThree = (el) => el > 3
const goodNumberRange = takor.allOf(lessThanTen, greaterThanThree)
// compose existing validator into another
const validNumRangeOrStr = takor.arrayOf(goodNumberRange, String)

validNumRangeOrStr([8, 4, 3.5, 5]) // true
validNumRangeOrStr([8, 4, '100', 5]) // true
validNumRangeOrStr([8, 4, 100, 5]) // false
validNumRangeOrStr(10) // false 
validNumRangeOrStr(new Map) // false

// takor.mapOf
const validMap = takor.mapOf(
    [Number, takor.oneOf(Array, Set)],
    [String, String]
)
validMap(new Map([ // true
    [10, []],
    [10, new Set],
    ['10', '10']
]))

validMap(new Map([ // false
    [10, []],
    [10, new Set],
    ['10', new Set]
]))

// takor.not
const nonNullOrArray = takor.not.oneOf(null, Array)
nonNullOrArray(10) // true
nonNullOrArray([]) // false


/**
 * Practical example:
 * Checks if the api response has a data key that is an array of objects
 * that have the keys id, phoneNumber, and name, where
 * the phone number is also checked if it is a valid phone number
 */
const isValidPhoneNumber = (phoneNumber) => {
    return /^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$/.test(phoneNumber)
}
const isExpectedApiResponse = takor.shape({
    status: Number,
    message: String,
    data: takor.arrayOf(takor.shape({
        id: Number,
        phoneNumber: isValidPhoneNumber,
        name: String,
    })),
})

Table of Contents

|static method| type| description | |--| -- | -- | |allOf| ListOfMatchers | Passed in validators must meet every criteria | |arrayOf| ListOfMatchers | Asserts the element is an array with specific types stated | |is| SingleMatcher | Performs an assertion on a single value | |mapOf| ListOfTupleMatchers | Asserts inner elemnts of an es6 Map | |oneOf| ListOfMatchers | At least one validator must match | |setOf| ListOfMatchers | Asserts inner elements of a Set | |shape| ShapeOfMatchers | Asserts a specific structure of a pojo (plain old javascript object) | |not.arrayOf | ListOfMatchers | The converse arrayOf | |not.is | SingleMatcher | The converse is | |not.mapOf | ListOfTupleMatchers | The converse mapOf | |not.oneOf | ListOfMatchers | The converse oneOf | |not.setOf | ListOfMatchers | The converse setOf | |not.shape | ShapeOfMatchers | The converse shape |

Available Matchers

Description

Out-of-the-box type validators. See examples for usage.

  • Number (Constructor)
  • String (Constructor)
  • Set (Constructor)
  • Map (Constructor)
  • Boolean (Constructor)
  • Array (Constructor)
  • Function (Constructor)
  • null
  • undefined
  • NaN
  • true
  • false

any

type: IAny

Desc

Not a validator. A function that is true for any value

Notes

Intended use is as an argument for validators.

Examples
takor.any(10) // true

const assertKeysOnly = takor.shape({
    data: takor.shape({
        entry: takor.any
    })
})

assertKeysOnly({ // true
    data: {
        entry: []
    }
})

falsey

type: IFalsey

Desc

Not a validator. It is a raw function that checks for any falsey value type

Notes

More information on falsey can be found on MDN Intended use is as an argument for validators.

Examples
takor.falsey(false) // true
takor.falsey([]) // false

const falseyOnly = takor.arrayOf(takor.falsey)
falseyOnly([1, null, '']) //  false
falseyOnly([null, 0, '']) //  true

pojo

type: IPojo

Desc

Not a validator. It is a raw function that if a value is a pojo. Use shape to specify a pojo shape

Notes

Intended use is as an argument for validators

Examples
takor.pojo({}) // true
takor.pojo(10) // false

// any object or array as long as top level key is `data`
const isPayload = takor.shape({
    data: takor.oneOf(takor.pojo, Array)
})

isPayload({ // false
    result: {}
})

isPayload({ // true
    data: {}
})

isPayload({ // true
    data: []
})

truthy

type: ITruthy

Desc

Not a validator. It is a function that checks if the value is truthy.

Notes

More information on truthy can be found on MDN Intended use is as an argument for validators.

Examples
takor.truthy(10) // true
takor.truthy(NaN) // false

const truthiesOnly = takor.arrayOf(takor.truthy)
truthiesOnly([1, new Set, '1']) //  true
truthiesOnly([1, new Set, '']) //  false

allOf

type: ListOfMatchers

Desc

Passed in validators must meet every criteria

Notes
  • Contradictory validators will result in false
Examples
const isPopulated = (arr) => arr.length > 0
const populatedStringArr = takor.allOf(takor.arrayOf(String), isPopulated)
populatedStringArr(['']) // true
populatedStringArr([]) // false
populatedStringArr([10]) // false

// contradictory types. impossible to meet criteria
const impossibleCheck = takor.allOf(Number, String)
impossibleCheck(10) // false
impossibleCheck('') // false

arrayOf

type: ListOfMatchers

Desc

Asserts the element is an array with specific types stated

Notes
Examples
const assert = takor.arrayOf(Number)
assert(['Number']) //false


const checkInnerOobj = takor.arrayOf(
    takor.shape({
        hi: Number
    }),
)
checkInnerOobj([{ hi: 10 }]) // true


const goodShapeNumOrStr = takor.arrayOf(
    takor.shape({
        hi: Number
    }),
    String,
    Number,
)
goodShapeNumOrStr([10, { hi: 10 }, new Map]) // false

const emptyArr = takor.arrayOf()
emptyArr([]) // true
emptyArr([1]) // false

is

type: SingleMatcher

Desc

Performs an assertion on a single value

Notes

This is a less flexible version of typeof. takor.is only accepts a single validator, while typeof accepts an arbitrary number.

Examples
const isStr = takor.is(String)
isStr('') // true
isStr(10) // false


// custom validator
takor.is((el) => el > 0)
enforcer(190) // true
enforcer(-1) // false

mapOf

type: ListOfTupleMatchers

Desc

Asserts inner elemnts of an es6 Map

Notes

mapOf takes an arbitrary number of tuples, correlating to [key, value] assertions

Examples
const allNumbers = takor.mapOf(
    [Number, Number]
)
allNumbers(new Map([ // true
    [10, 10]
]))
allNumbers(new Map([ // false
    [10, '10']
]))


// inner assertion inside mapOf
const valueMapShape = takor.shape({
    key: takor.shape({
        key2: takor.oneOf(null, Number)
    })
})
const checkMapValue = takor.mapOf(
    [String, assertShape]
)
checkMapValue(new Map([ // true
    ['12', { key: { key2: null } }],
])))

not.arrayOf

type: ListOfMatchers

Desc

The converse arrayOf

Notes

Any non-Array value will be true

Examples
const checkArray = takor.not.arrayOf(Array, String, Set)
checkArray([[]]) // false
checkArray([10]) // true
checkArray(NaN) // true

not.is

type: SingleMatcher

Desc

The converse is

Notes
Examples
const checkStrLen = takor.not.is((str) => str.length === 0)
checkStrLen('') // false
checkStrLen('10') // true

not.mapOf

type: ListOfTupleMatchers

Desc

The converse mapOf

Notes

non-Map values will always be true

Examples
const notAllNumbers = takor.not.mapOf(
    [Number, Number]
)

notAllNumbers(new Map([ // false
    [10, 10]
]))
notAllNumbers(new Map([ // true
    [10, '10']
]))

not.oneOf

type: ListOfMatchers

Desc

The converse oneOf

Notes
Examples
const keyIsNotNull = takor.shape({
    key: takor.not.oneOf(null)
})

keyIsNotNull(({ // true
    key: 10
}))

not.setOf

type: ListOfMatchers

Desc

The converse setOf

Notes
Examples
const notStrs = takor.not.setOf(String)
notStrs(new Set([10])) // true
notStrs(new Set([10, '12'])) // false

not.shape

type: ShapeOfMatchers

Desc

The converse shape

Notes
Examples
const notEmpty = takor.not.shape({})
notEmpty({ a: 10 }) // true
notEmpty({}) // false

oneOf

type: ListOfMatchers

Desc

At least one validator must match

Notes
Examples
const numOrStr = takor.oneOf(String, Number)
numOrStr(10) // true
numOrStr(new Set) // false

// check inner elements of an array
const dogOrCat = takor.oneOf('dog', 'cat')
dogOrCat('cat') // true
dogOrCat('bird') // false

setOf

type: ListOfMatchers

Desc

Asserts inner elements of a Set

Notes
Examples
const allNums = takor.setOf(Number)
setOfNums(new Set([10, 30, 40])) // true

const strOrNums = takor.setOf(String, Number)
strOrNums(new Set([10, '12'])) // true
strOrNums(new Set([10, '12', new Map])) // false

shape

type: ShapeOfMatchers

Desc

Asserts a specific structure of a pojo (plain old javascript object)

Notes
  • shape checks only top level keys.
  • Nest another shape to assert deeper
  • Does not check for superfluous keys. Only ensures types of defined keys
Examples
const assert = takor.shape({
    key: String
})
assert({key: ''}) // true


const assertNested = takor.shape({
    key: takor.shape({
        key: takor.shape({
            key1: String
        })
    })
})

assertNested({ // false
    key: {
        els: '10'
    }
})

const assertApiPayload = takor.shape({
    data: takor.arrayOf(takor.shape({
        user: takor.shape({
            phoneNumber: Number,
            firstName: String
        })
    }))
})

assertApiPayload({ // true
    data: [{
        user: {
            phoneNumber: 4024224856,
            firstName: 'john',
            lastName: 'smith' // does not check this key
        }
    }]
})

types

// Custom validators are any function that accepts an input value and returns a boolean
type ICustomValidator = (el: any) => boolean

// Baked in common matchers, so you don't have to constantly write them yourself
type IPojo = (el: any) => boolean
type IAny = (el: any) => boolean
type ITruthy = (el: any) => boolean
type IFalsey = (el: any) => boolean

// Shape accepts an object where the value is a validator or a primitive literal
type ShapeOfMatchers = {
    [key: string]: IMatcher
}

// A function that takes an arbitrary number of matchers that returns another function to assert values
type ListOfMatchers = (...matchers: IMatcher[]) => (el: any) => boolean

// A function that takes an arbitrary number of tuple of matchers that return another function to assert values
type ListOfTupleMatchers = (...matchers: [IMatcher, IMatcher][]) => (el: any) => boolean

// Only accepts a single matcher
type SingleMatcher = (matcher: IMatcher) => (el: any) => boolean

// List of valid matchers
type IMatcher =
    SetConstructor |
    MapConstructor |
    NumberConstructor |
    StringConstructor |
    null |
    undefined |
    ArrayConstructor |
    BooleanConstructor |
    true |
    false |
    string |
    number |
    ICustomValidator | // custom validator
    IPojo | // built in matcher
    IAny | // built in matcher
    ITruthy | // built in matcher
    IFalsey // built in matcher