|
|
# is What? 🙉
<a href="https://www.npmjs.com/package/is-what"><img src="https://img.shields.io/npm/v/is-what.svg" alt="Total Downloads"></a><a href="https://www.npmjs.com/package/is-what"><img src="https://img.shields.io/npm/dw/is-what.svg" alt="Latest Stable Version"></a>
Very simple & small JS type check functions. It's fully TypeScript supported!
```npm i is-what```
Or for deno available at: `"deno.land/x/is_what"`
> Also check out [is-where 🙈](https://github.com/mesqueeb/is-where)
## Motivation
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 😃)
## Usage
is-what is really easy to use, and most functions work just like you'd expect.
```js// import functions you want to use like so:import { isString, isDate, isPlainObject } from 'is-what'```
1. First I'll go over the simple functions available. Only `isNumber` and `isDate` have special treatment.2. After that I'll talk about working with Objects (plain objects vs class instances etc.).3. Lastly I'll talk about TypeScript implementation
### Simple type check functions
```js// basicsisBoolean(true) // trueisBoolean(false) // trueisUndefined(undefined) // trueisNull(null) // true
// stringsisString('') // trueisEmptyString('') // trueisFullString('') // falseisHexDecimal('60adf084f0fbdcab42de841e') // trueisHexDecimal('60adf084f0fbdcab42de841e', 24) // check specific length of 24 (eg. MongoDB ObjectId)
// numbersisNumber(0) // trueisNumber('0') // falseisNumber(NaN) // false *isPositiveNumber(1) // trueisNegativeNumber(-1) // true// * see below for special NaN use cases!
// arraysisArray([]) // trueisEmptyArray([]) // trueisFullArray([1]) // true
// objectsisPlainObject({}) // true *isEmptyObject({}) // trueisFullObject({ a: 1 }) // true// * see below for special object (& class instance) use cases!
// functionsisFunction(function () {}) // trueisFunction(() => {}) // true
// datesisDate(new Date()) // trueisDate(new Date('invalid date')) // false
// maps & setsisMap(new Map()) // trueisSet(new Set()) // trueisWeakMap(new WeakMap()) // trueisWeakSet(new WeakSet()) // true
// othersisRegExp(/\s/gi) // trueisSymbol(Symbol()) // trueisBlob(new Blob()) // trueisFile(new File([''], '', { type: 'text/html' })) // trueisError(new Error('')) // trueisPromise(new Promise((resolve) => {})) // true
// primitivesisPrimitive('') // true// true for any of: boolean, null, undefined, number, string, symbol
// iterablesisIterable([1, 2, 3]) // trueisIterable('hello') // trueisIterable(new Map()) // trueisIterable(new Set()) // trueisIterable(function* generator() { yield 1}) // true```
### Let's talk about NaN
`isNaN` is a built-in JS Function but it really makes no sense:
```js// 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:
```jsimport { 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```
### isPlainObject vs isAnyObject
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.
```js// define a plain objectconst plainObject = { hello: 'I am a good old object.' }
// define a special objectclass SpecialObject { constructor(somethingSpecial) { this.speciality = somethingSpecial }}const specialObject = new SpecialObject('I am a special object! I am a class instance!!!')
// check the plain objectisPlainObject(plainObject) // returns trueisAnyObject(plainObject) // returns truegetType(plainObject) // returns 'Object'
// check the special objectisPlainObject(specialObject) // returns false !!!!!!!!!isAnyObject(specialObject) // returns truegetType(specialObject) // returns 'Object'```
> Please note that `isPlainObject` will only return `true` for normal plain JavaScript objects.
### Getting and checking for specific types
You can check for specific types with `getType` and `isType`:
```jsimport { getType, isType } from 'is-what'
getType('') // returns 'String'// pass a Type as second param:isType('', String) // returns true```
If you just want to make sure your object _inherits_ from a particular class or`toStringTag` value, you can use `isInstanceOf()` like this:
```jsimport { isInstanceOf } from 'is-what'
isInstanceOf(new XMLHttpRequest(), 'EventTarget')// returns trueisInstanceOf(globalThis, ReadableStream)// returns false```
## TypeScript
is-what makes TypeScript know the type during if statements. This means that a check returns the type of the payload for TypeScript users.
```tsfunction isNumber(payload: unknown): payload is number { // return boolean}// As you can see above, all functions return a boolean for JavaScript, but pass the payload type to 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:
```tsfunction 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 doif (isPlainObject(payload) && payload.id) return payload.id// if isPlainObject() would return `payload is object` then it would give an error at `payload.id````
### isObjectLike
If you want more control over what kind of interface/type is casted when checking for objects.
To cast to a specific type while checking for `isAnyObject`, can use `isObjectLike<T>`:
```tsimport { 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 returnsif (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:
```tsfunction isObjectLike<T extends object>(payload: unknown): payload is T { return isAnyObject(payload)}```
## Meet the family (more tiny utils with TS support)
- [is-what 🙉](https://github.com/mesqueeb/is-what)- [is-where 🙈](https://github.com/mesqueeb/is-where)- [merge-anything 🥡](https://github.com/mesqueeb/merge-anything)- [check-anything 👁](https://github.com/mesqueeb/check-anything)- [remove-anything ✂️](https://github.com/mesqueeb/remove-anything)- [getorset-anything 🐊](https://github.com/mesqueeb/getorset-anything)- [map-anything 🗺](https://github.com/mesqueeb/map-anything)- [filter-anything ⚔️](https://github.com/mesqueeb/filter-anything)- [copy-anything 🎭](https://github.com/mesqueeb/copy-anything)- [case-anything 🐫](https://github.com/mesqueeb/case-anything)- [flatten-anything 🏏](https://github.com/mesqueeb/flatten-anything)- [nestify-anything 🧅](https://github.com/mesqueeb/nestify-anything)
## Source code
It's litterally just these functions:
```jsfunction 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](https://github.com/mesqueeb/is-what/blob/main/src/index.ts).
|