Options
All
  • Public
  • Public/Protected
  • All
Menu

JTY Logo

Build Status GitHub issues GitHub forks GitHub stars GitHub license Vulnerabilities Downloads

JTY - JavaScript typecheck

A minimalistic library for writing safer code. It came out of a few years of programming JavaScript and TypeScript where I wrote these functions over and over to ensure code reliability.

It has a solid and minimalistic API surface that provides useful functions for basic type checking.

  • No dependencies
  • Complements what's available in JavaScript
  • All functions return true or false (none of them throws in any condition)
  • It is resistent to monkey patching or malicious prototype overriding
  • It comes with TypeScript support out of the box.
  • Thoroughly tested for edge cases
  • Works in Node and Browsers (CommonJS out of the box)
  • High performance

Why?

  • For JavaScript, jty helps verify function/method contracts and fail early with good error messages instead of continuing on wrong assumption and producing wrong results (which is hard to debug due to implicit type conversion quirks)
  • For TypeScript, jty helps guarantee type safely when called from JavaScript code (also provides reliability against abusing TypeScript's escape hatches like as and any)
  • For JSON, jty helps verify the shape of the object inside the code without having to write a schema.

How to use it?

$ npm i jty
// In your JS file
const { isStr } = require('jty')

if (isStr('Hello world!', 3)) {
    console.log('Success')
} else {
    throw new TypeError('Expected an string with at least 3 characters')
}

If you use TypeScript, many of these functions work as guards:

const a = { foo: 13 }
if (hasPath(a, 'bar', 'baz')) {
  // `a.foo` is valid, as well as `a.bar` and `a.baz`
}

Let's say you have a function that is supposed to double a number:

function double(n) {
    return n + n
}
double(1)  // 2
double(13) // 26

But this function happily accepts strings which is not desired:

double('13') // '1313'

Using jty we can verify the input before using it:

const { isNum } = require('jty')

function double(n) {
    if (isNum(n)) {
        return n + n
    }
    throw new TypeError(`Expected a number but got ${n}`)
}

double(13)   // 26
double('13') // throws 'Expected a number but got 13'
double(NaN)  // throws 'Expected a number but got NaN'

You can also use the assertion library of your choice to make the code shorter and more readable:

// Node assert: https://nodejs.org/api/assert.html
const assert = require('assert')
const { isNum } = require('jty')

function double(n) {
    assert(isNum(n))
    return n + n
}

API

On Github Pages

Tips

  • Every value should be validated close to where it is used. This usually means that the functions should validate the parameters that the logic in their own body depends on.
  • ALWAYS validate the shape and values of any input from external systems (users, other servers, even local JSON files that are not validated against a schema and/or don't live in the same repo as your code)
  • If you're using TypeScript, use type guards to programmatically ensure the shape of a value (particularly objects)
  • If you are writing TypeScript code that is going to be called from JavaScript, always verify the input of the user-facing APIs
  • Use JavaScript's own language constructs whenever it makes sense
    • ['possibleValue1', 'possibleValue2'].includes(x) to check if x is any of the possible values (kinda like TypeScript's union types)
    • someArray.every(item => checkItemFormat(item)) to check that all elements of an array have a certain shape
    • isStr(x) || isInt(x) check OR to ensure that a value is of either types
    • a instanceOf A ensure that a is an instance of the class A
    • Use the in operator with caution: user in objectWithOneKeyPerUser returns true if the user is 'constructor' (use hasOProp() instead)
    • Use console.assert()
  • Don't check the shape of objects that are guaranteed to have a certain form. For example in function f(...props) { if (Array.isArr(props)) ... }, props is guaranteed to be an array per JavaScript language specification.
  • If a function merely forwards a parameter to an inner function where it's used, it's best for the inner function to validates it.
  • A good error message should have enough information to facilitate inspection and troubleshooting.
  • When throwing an error, use an appropriate JavaScript standard Error subclass:
    • TypeError when a value has an unexpected type
    • ReferenceError when a property is missing from an object
    • RangeError when a value is outside the expected range
    • SyntaxError when there is a syntax error (usually comes handy when parsing a string, using regular expressions or validating JSON)
    • See more Error types on MDN

Made in Sweden 🇸🇪 by Alex Ewerlöf

Index

Type aliases

ObjectProp

ObjectProp: string | number | symbol

Acceptable types for object property names

Functions

hasOPath

  • hasOPath<K>(x: unknown, ...propNames: keyof []): x is Record<K[0], Record<K[1], Record<K[2], Record<K[3], Record<K[4], Record<K[5], Record<K[6], Record<K[7], Record<K[8], Record<K[9], object>>>>>>>>>>
  • Similar to hasPath but only works for own properties (not inherited properties)

    Type parameters

    Parameters

    • x: unknown

      a value that may possibly have some properties

    • Rest ...propNames: keyof []

      one or more property names

    Returns x is Record<K[0], Record<K[1], Record<K[2], Record<K[3], Record<K[4], Record<K[5], Record<K[6], Record<K[7], Record<K[8], Record<K[9], object>>>>>>>>>>

hasOProp

  • hasOProp<K>(x: unknown, ...propNames: keyof K[]): x is Record<K, any>
  • Same as hasProp but checks for own properties (not inherited properties)

    Type parameters

    Parameters

    • x: unknown

      an object

    • Rest ...propNames: keyof K[]

      one or more property names

    Returns x is Record<K, any>

hasPath

  • hasPath<K>(x: unknown, ...propNames: keyof []): x is Record<K[0], Record<K[1], Record<K[2], Record<K[3], Record<K[4], Record<K[5], Record<K[6], Record<K[7], Record<K[8], Record<K[9], object>>>>>>>>>>
  • Checks if the provided value has the a path of properties

    example

    given x = { foo: { bar: { baz: undefined }}}

    • hasPath(x, 'foo', 'bar') returns true
    • hasPath(x, 'foo', 'bar', 'baz') returns true because x.foo.bar.baz property exists
    • hasPath(x, 'foo', 'hello', 'baz') returns false because x.foo.hello property does not exist
    • hasPath(x, 'foo', 'bar', 'hello') returns false

    Type parameters

    Parameters

    • x: unknown

      a value that may possibly have some properties

    • Rest ...propNames: keyof []

      one or more property names

    Returns x is Record<K[0], Record<K[1], Record<K[2], Record<K[3], Record<K[4], Record<K[5], Record<K[6], Record<K[7], Record<K[8], Record<K[9], object>>>>>>>>>>

hasProp

  • hasProp<K>(x: unknown, ...propNames: keyof K[]): x is Record<K, object>
  • Checks if x is a non-null object that has all the provided properties

    example

    given a = { b: undefined, c:[0, 1, 2]}

    • hasProp(a, 'b') => true
    • hasProp(a, 'b', 'c') => true because both a.b and a.c properties exist
    • hasProp(a.c, '0', 1, 'length') => true because a.c is an array that has all those properties

    Type parameters

    Parameters

    • x: unknown

      an object

    • Rest ...propNames: keyof K[]

      one or more property names

    Returns x is Record<K, object>

isA

  • isA<T>(x: unknown, classConstructor: T): x is InstanceType<T>
  • Checks if a provided value is an instance of the provided class

    This does not throw for some cases where JavaScript chokes ()

    example

    isA({}, Object) => true

    example

    isA(/hello/i, RegExp) => true

    example

    isA(Promise.resolve, Promise) => true

    example

    isA('plain str', String) => false

    example

    isA(new String('str obj'), String) => true

    example

    isA(22, Number) => false

    example

    isA(new Number(33), Number) => true

    example

    2 instanceof NaN throws a TypeError but isA(2, NaN) returns false

    Type parameters

    • T: {}

    Parameters

    • x: unknown

      possibly an instance of a class

    • classConstructor: T

      a class constructor (usually starts with big letter!)

    Returns x is InstanceType<T>

isArr

  • isArr(x: unknown, minLen?: number, maxLen?: undefined | number): x is unknown[]
  • Checks if the provided value is an array and optionally checks whether its length is in a boundary

    Parameters

    • x: unknown

      possibly a string

    • Default value minLen: number = 0

      minimum possible length (inclusive)

    • Optional maxLen: undefined | number

      maximum possible length (inclusive)

    Returns x is unknown[]

isBool

  • isBool(x: unknown): x is boolean
  • Checks if the provided value is boolean (basically true or false)

    This is exactly typeof x === "boolean" but a bit shorter

    Parameters

    • x: unknown

      possibly a boolean value

    Returns x is boolean

isDef

  • isDef(x: unknown): x is Exclude<any, undefined>
  • Checks if the provided value is defined This is exactly x !== undefined but a bit shorter

    Also

    see

    isUndef

    Parameters

    • x: unknown

      any value

    Returns x is Exclude<any, undefined>

isFn

  • isFn<T>(x: unknown): x is T
  • Checks if a value is a function

    This is exactly typeof x === "function" but a bit shorter

    If you are using TypeScript and you know the function signature, you can provide the generic T for your guard.

    Type parameters

    • T: Function

    Parameters

    • x: unknown

      possibly a function (including static methods but not getters or setters)

    Returns x is T

    true if the value is a function, false otherwise

isInt

  • isInt(x: unknown, min?: undefined | number, max?: undefined | number): x is number
  • Checks if a value is a finite integer number and optionally bound by a min and max.

    example

    isInt(3.14) => false

    example

    isInt(3) => true

    Parameters

    • x: unknown

      possibly an integer number

    • Optional min: undefined | number

      the minimum possible value (inclusive). If this is not a finite number, the lower bound will not be checked

    • Optional max: undefined | number

      the maximum possible value (inclusive). If this is not a finite number, the upper bound will not be checked

    Returns x is number

isNum

  • isNum(x: unknown, min?: undefined | number, max?: undefined | number): x is number
  • Checks if a value is a finite number and optionally bound by a min and max

    example

    isNum(3) => true

    example

    isNum(3, 3) => true

    example

    isNum(3, 10) => false

    example

    isNum(3, 3, 5) => true

    example

    isNum(3, 10, 15) => false

    example

    isNum(3, undefined, 5) => true

    example

    isNum('3') => false

    example

    isNum(NaN) => false

    Parameters

    • x: unknown

      possibly a number

    • Optional min: undefined | number

      the minimum possible value (inclusive). If this is not a finite number, the lower bound will not be checked

    • Optional max: undefined | number

      the maximum possible value (inclusive). If this is not a finite number, the upper bound will not be checked

    Returns x is number

isObj

  • isObj(x: unknown): x is Exclude<object, null>
  • Checks if a value is a non-null object

    example

    isObj({}) => true

    example

    isObj(null) => false

    example

    isObj([]) => true

    example

    isObj(new URL) => true

    example

    isObj(13) => false

    example

    isObj(Number(13)) => false

    Parameters

    • x: unknown

      possibly an object

    Returns x is Exclude<object, null>

    true if the value is an non-null object, false otherwise

isStr

  • isStr(x: unknown, minLen?: number, maxLen?: undefined | number): x is string
  • Checks if the provided value is a string and optionally checks whether its length is in a boundary

    Parameters

    • x: unknown

      possibly a string

    • Default value minLen: number = 0

      minimum possible length (inclusive)

    • Optional maxLen: undefined | number

      maximum possible length (inclusive)

    Returns x is string

isSym

  • isSym(x: unknown): x is symbol
  • Checks if the provided value is a symbol

    This is exactly x === "symbol" but a bit shorter

    Parameters

    • x: unknown

      possibly a symbol

    Returns x is symbol

isUndef

  • isUndef(x: unknown): x is undefined
  • Checks if the provided value is defined

    This is exactly x === undefined but a bit shorter

    Also see isDef

    Parameters

    • x: unknown

      any value

    Returns x is undefined