UNPKG

7.5 kBMarkdownView Raw
1# is What? 🙉
2
3Very simple & small JS type check functions. It's fully TypeScript supported!
4
5```
6npm i is-what
7```
8
9Or for deno available at: `"deno.land/x/is_what"`
10
11## Motivation
12
13I built is-what because the existing solutions were all too complex or too poorly built.
14
15I was looking for:
16
17- A simple way to check any kind of type (including non-primitives)
18- Be able to check if an object is a plain object `{}` or a special object (like a class instance) ‼️
19- Let TypeScript automatically know what type a value is when checking
20
21And that's exactly what `is-what` is! (what a great wordplay 😃)
22
23## Usage
24
25is-what is really easy to use, and most functions work just like you'd expect.
26
27```js
28// import functions you want to use like so:
29import { isString, isDate, isPlainObject } from 'is-what'
30```
31
321. First I'll go over the simple functions available. Only `isNumber` and `isDate` have special treatment.
332. After that I'll talk about working with Objects (plain objects vs class instances etc.).
343. Lastly I'll talk about TypeScript implementation
35
36### Simple type check functions
37
38```js
39// basics
40isBoolean(true) // true
41isBoolean(false) // true
42isUndefined(undefined) // true
43isNull(null) // true
44
45// strings
46isString('') // true
47isEmptyString('') // true
48isFullString('') // false
49
50// numbers
51isNumber(0) // true
52isNumber('0') // false
53isNumber(NaN) // false *
54isPositiveNumber(1) // true
55isNegativeNumber(-1) // true
56// * see below for special NaN use cases!
57
58// arrays
59isArray([]) // true
60isEmptyArray([]) // true
61isFullArray([1]) // true
62
63// objects
64isPlainObject({}) // true *
65isEmptyObject({}) // true
66isFullObject({ a: 1 }) // true
67// * see below for special object (& class instance) use cases!
68
69// functions
70isFunction(function () {}) // true
71isFunction(() => {}) // true
72
73// dates
74isDate(new Date()) // true
75isDate(new Date('invalid date')) // false
76
77// maps & sets
78isMap(new Map()) // true
79isSet(new Set()) // true
80isWeakMap(new WeakMap()) // true
81isWeakSet(new WeakSet()) // true
82
83// others
84isRegExp(/\s/gi) // true
85isSymbol(Symbol()) // true
86isBlob(new Blob()) // true
87isFile(new File([''], '', { type: 'text/html' })) // true
88isError(new Error('')) // true
89isPromise(new Promise((resolve) => {})) // true
90
91// primitives
92isPrimitive('') // true
93// true for any of: boolean, null, undefined, number, string, symbol
94```
95
96### Let's talk about NaN
97
98`isNaN` is a built-in JS Function but it really makes no sense:
99
100```js
101// 1)
102typeof NaN === 'number' // true
103// 🤔 ("not a number" is a "number"...)
104
105// 2)
106isNaN('1') // false
107// 🤔 the string '1' is not-"not a number"... so it's a number??
108
109// 3)
110isNaN('one') // true
111// 🤔 'one' is NaN but `NaN === 'one'` is false...
112```
113
114With is-what the way we treat NaN makes a little bit more sense:
115
116```js
117import { isNumber, isNaNValue } from 'is-what'
118
119// 1)
120isNumber(NaN) // false!
121// let's not treat NaN as a number
122
123// 2)
124isNaNValue('1') // false
125// if it's not NaN, it's not NaN!!
126
127// 3)
128isNaNValue('one') // false
129// if it's not NaN, it's not NaN!!
130
131isNaNValue(NaN) // true
132```
133
134### isPlainObject vs isAnyObject
135
136Checking 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:
137
138- `isPlainObject` will only return `true` on plain JavaScript objects and not on classes or others
139- `isAnyObject` will be more loose and return `true` on regular objects, classes, etc.
140
141```js
142// define a plain object
143const plainObject = { hello: 'I am a good old object.' }
144
145// define a special object
146class SpecialObject {
147 constructor(somethingSpecial) {
148 this.speciality = somethingSpecial
149 }
150}
151const specialObject = new SpecialObject('I am a special object! I am a class instance!!!')
152
153// check the plain object
154isPlainObject(plainObject) // returns true
155isAnyObject(plainObject) // returns true
156getType(plainObject) // returns 'Object'
157
158// check the special object
159isPlainObject(specialObject) // returns false !!!!!!!!!
160isAnyObject(specialObject) // returns true
161getType(specialObject) // returns 'Object'
162```
163
164> Please note that `isPlainObject` will only return `true` for normal plain JavaScript objects.
165
166### Getting and checking for specific types
167
168You can check for specific types with `getType` and `isType`:
169
170```js
171import { getType, isType } from 'is-what'
172
173getType('') // returns 'String'
174// pass a Type as second param:
175isType('', String) // returns true
176```
177
178## TypeScript
179
180is-what makes TypeScript know the type during if statements. This means that a check returns the type of the payload for TypeScript users.
181
182```ts
183function isNumber(payload: any): payload is number {
184 // return boolean
185}
186// As you can see above, all functions return a boolean for JavaScript, but pass the payload type to TypeScript.
187
188// usage example:
189function fn(payload: string | number): number {
190 if (isNumber(payload)) {
191 // ↑ TypeScript already knows payload is a number here!
192 return payload
193 }
194 return 0
195}
196```
197
198`isPlainObject` and `isAnyObject` with TypeScript will declare the payload to be an object type with any props:
199
200```ts
201function isPlainObject(payload: any): payload is { [key: string]: any }
202function isAnyObject(payload: any): payload is { [key: string]: any }
203// The reason to return `{[key: string]: any}` is to be able to do
204if (isPlainObject(payload) && payload.id) return payload.id
205// if isPlainObject() would return `payload is object` then it would give an error at `payload.id`
206```
207
208### isObjectLike
209
210If you want more control over what kind of interface/type is casted when checking for objects.
211
212To cast to a specific type while checking for `isAnyObject`, can use `isObjectLike<T>`:
213
214```ts
215import { isObjectLike } from 'is-what'
216
217const payload = { name: 'Mesqueeb' } // current type: `{ name: string }`
218
219// Without casting:
220if (isAnyObject(payload)) {
221 // in here `payload` is casted to: `Record<string | number | symbol, any>`
222 // WE LOOSE THE TYPE!
223}
224
225// With casting:
226// you can pass a specific type for TS that will be casted when the function returns
227if (isObjectLike<{ name: string }>(payload)) {
228 // in here `payload` is casted to: `{ name: string }`
229}
230```
231
232Please note: this library will not actually check the shape of the object, you need to do that yourself.
233
234`isObjectLike<T>` works like this under the hood:
235
236```ts
237function isObjectLike<T extends object>(payload: any): payload is T {
238 return isAnyObject(payload)
239}
240```
241
242## Meet the family
243
244- [is-what 🙉](https://github.com/mesqueeb/is-what)
245- [merge-anything 🥡](https://github.com/mesqueeb/merge-anything)
246- [filter-anything ⚔️](https://github.com/mesqueeb/filter-anything)
247- [find-and-replace-anything 🎣](https://github.com/mesqueeb/find-and-replace-anything)
248- [compare-anything 🛰](https://github.com/mesqueeb/compare-anything)
249- [copy-anything 🎭](https://github.com/mesqueeb/copy-anything)
250- [flatten-anything 🏏](https://github.com/mesqueeb/flatten-anything)
251
252## Source code
253
254It's litterally just these functions:
255
256```js
257function getType(payload) {
258 return Object.prototype.toString.call(payload).slice(8, -1)
259}
260function isUndefined(payload) {
261 return getType(payload) === 'Undefined'
262}
263function isString(payload) {
264 return getType(payload) === 'String'
265}
266function isAnyObject(payload) {
267 return getType(payload) === 'Object'
268}
269// etc...
270```
271
272See the full source code [here](https://github.com/mesqueeb/is-what/blob/master/src/index.ts).