is-what
    Preparing search index...

    is-what

    is What? 🙉

    npm version npm downloads/week

    Tiny, zero-dependency JavaScript type guards with first-class TypeScript support. Tree-shakable ESM by default.

    npm i is-what
    

    Or for deno available at: "deno.land/x/is_what"

    Also check out is-where 🙈

    I built is-what because the existing solutions were all too complex or too poorly built.

    I was looking for:

    • A simple way to check any kind of type (including non-primitives)
    • Be able to check if an object is a plain object {} or a special object (like a class instance) ‼️
    • Let TypeScript automatically know what type a value is when checking

    And that's exactly what is-what is! (what a great wordplay 😃)

    is-what is really easy to use, and most functions work just like you'd expect.

    // import functions you want to use like so:
    import { isString, isDate, isPlainObject } from 'is-what'
    1. Below are the most common functions (with a special note on isNumber and isDate).
    2. Then we cover Objects (plain objects vs class instances).
    3. Finally we show TypeScript narrowing and isObjectLike<T>.
    // basics
    isBoolean(true) // true
    isBoolean(false) // true
    isUndefined(undefined) // true
    isNull(null) // true

    // strings
    isString('') // true
    isEmptyString('') // true
    isFullString('') // false
    isHexDecimal('60adf084f0fbdcab42de841e') // true
    isHexDecimal('60adf084f0fbdcab42de841e', 24) // check specific length of 24 (eg. MongoDB ObjectId)

    // numbers
    isNumber(0) // true
    isNumber('0') // false
    isNumber(NaN) // false *
    isPositiveNumber(1) // true
    isNegativeNumber(-1) // true
    // * see below for special NaN use cases!

    // arrays
    isArray([]) // true
    isEmptyArray([]) // true
    isFullArray([1]) // true

    // objects
    isPlainObject({}) // true *
    isEmptyObject({}) // true
    isFullObject({ a: 1 }) // true
    // * see below for special object (& class instance) use cases!

    // functions
    isFunction(function () {}) // true
    isFunction(() => {}) // true

    // dates
    isDate(new Date()) // true
    isDate(new Date('invalid date')) // false

    // maps & sets
    isMap(new Map()) // true
    isSet(new Set()) // true
    isWeakMap(new WeakMap()) // true
    isWeakSet(new WeakSet()) // true

    // others
    isRegExp(/\s/gi) // true
    isSymbol(Symbol()) // true
    isBlob(new Blob()) // true
    isFile(new File([''], '', { type: 'text/html' })) // true
    isError(new Error('')) // true
    isPromise(new Promise((resolve) => {})) // true

    // primitives
    isPrimitive('') // true
    // true for any of: boolean, null, undefined, number, string, symbol

    // iterables
    isIterable([1, 2, 3]) // true
    isIterable('hello') // true
    isIterable(new Map()) // true
    isIterable(new Set()) // true
    isIterable(function* generator() {
    yield 1
    }) // true

    isNaN is a built-in JS Function but it really makes no sense:

    // 1)
    typeof NaN === 'number' // true
    // 🤔 ("not a number" is a "number"...)

    // 2)
    isNaN('1') // false
    // 🤔 the string '1' is not-"not a number"... so it's a number??

    // 3)
    isNaN('one') // true
    // 🤔 'one' is NaN but `NaN === 'one'` is false...

    With is-what the way we treat NaN makes a little bit more sense:

    import { isNumber, isNaNValue } from 'is-what'

    // 1)
    isNumber(NaN) // false!
    // let's not treat NaN as a number

    // 2)
    isNaNValue('1') // false
    // if it's not NaN, it's not NaN!!

    // 3)
    isNaNValue('one') // false
    // if it's not NaN, it's not NaN!!

    isNaNValue(NaN) // true

    Checking for a JavaScript object can be really difficult. In JavaScript you can create classes that will behave just like JavaScript objects but might have completely different prototypes. With is-what I went for this classification:

    • isPlainObject will only return true on plain JavaScript objects and not on classes or others
    • isAnyObject will be more loose and return true on regular objects, classes, etc.
    // define a plain object
    const plainObject = { hello: 'I am a good old object.' }

    // define a special object
    class SpecialObject {
    constructor(somethingSpecial) {
    this.speciality = somethingSpecial
    }
    }
    const specialObject = new SpecialObject('I am a special object! I am a class instance!!!')

    // check the plain object
    isPlainObject(plainObject) // returns true
    isAnyObject(plainObject) // returns true
    getType(plainObject) // returns 'Object'

    // check the special object
    isPlainObject(specialObject) // returns false !!!!!!!!!
    isAnyObject(specialObject) // returns true
    getType(specialObject) // returns 'Object'

    Please note that isPlainObject returns true only for plain JavaScript objects.

    You can check for specific types with getType and isType:

    import { getType, isType } from 'is-what'

    getType('') // returns 'String'
    // pass a Type as second param:
    isType('', String) // returns true

    If you want to make sure your object inherits from a particular class or matches a toStringTag value, use isInstanceOf():

    import { isInstanceOf } from 'is-what'

    isInstanceOf(new XMLHttpRequest(), 'EventTarget')
    // returns true
    isInstanceOf(globalThis, ReadableStream)
    // returns false

    is-what makes TypeScript know the type during if statements. This means that a check returns the type of the payload for TypeScript users.

    function isNumber(payload: unknown): payload is number {
    // return boolean
    }
    // All functions return a boolean for JavaScript, but act as type guards for TypeScript.

    // usage example:
    function fn(payload: string | number): number {
    if (isNumber(payload)) {
    // ↑ TypeScript already knows payload is a number here!
    return payload
    }
    return 0
    }

    isPlainObject and isAnyObject with TypeScript will declare the payload to be an object type with any props:

    function isPlainObject(payload: unknown): payload is { [key: string]: unknown }
    function isAnyObject(payload: unknown): payload is { [key: string]: unknown }
    // The reason to return `{[key: string]: unknown}` is to be able to do
    if (isPlainObject(payload) && payload.id) return payload.id
    // if isPlainObject() would return `payload is object` then it would give an error at `payload.id`

    If you want more control over what kind of interface/type is cast when checking for objects:

    To cast to a specific type while checking for isAnyObject, can use isObjectLike<T>:

    import { isObjectLike } from 'is-what'

    const payload = { name: 'Mesqueeb' } // current type: `{ name: string }`

    // Without casting:
    if (isAnyObject(payload)) {
    // in here `payload` is casted to: `Record<string | number | symbol, unknown>`
    // WE LOOSE THE TYPE!
    }

    // With casting:
    // you can pass a specific type for TS that will be casted when the function returns
    if (isObjectLike<{ name: string }>(payload)) {
    // in here `payload` is casted to: `{ name: string }`
    }

    Please note: this library will not actually check the shape of the object, you need to do that yourself.

    isObjectLike<T> works like this under the hood:

    function isObjectLike<T extends object>(payload: unknown): payload is T {
    return isAnyObject(payload)
    }

    It's litterally just these functions:

    function getType(payload) {
    return Object.prototype.toString.call(payload).slice(8, -1)
    }
    function isUndefined(payload) {
    return getType(payload) === 'Undefined'
    }
    function isString(payload) {
    return getType(payload) === 'String'
    }
    function isAnyObject(payload) {
    return getType(payload) === 'Object'
    }
    // etc...

    See the full source code here.