is-what 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +3 -0
- package/.eslintignore +9 -0
- package/.github/FUNDING.yml +12 -0
- package/.prettierrc +9 -0
- package/.vscode/settings.json +9 -0
- package/LICENSE +21 -0
- package/README.md +272 -0
- package/build.js +40 -0
- package/dist/index.js +343 -0
- package/package.json +105 -0
- package/src/index.ts +415 -0
- package/test/ava.ts +419 -0
- package/tsconfig.json +15 -0
- package/types/index.d.ts +267 -0
package/.babelrc
ADDED
package/.eslintignore
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# These are supported funding model platforms
|
2
|
+
|
3
|
+
github: mesqueeb
|
4
|
+
patreon: # Replace with a single Patreon username
|
5
|
+
open_collective: # Replace with a single Open Collective username
|
6
|
+
ko_fi: # Replace with a single Ko-fi username
|
7
|
+
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
8
|
+
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
9
|
+
liberapay: # Replace with a single Liberapay username
|
10
|
+
issuehunt: # Replace with a single IssueHunt username
|
11
|
+
otechie: # Replace with a single Otechie username
|
12
|
+
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
package/.prettierrc
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
{
|
2
|
+
"editor.formatOnSave": true,
|
3
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
4
|
+
"editor.tabSize": 2, // make sure this is the same as .prettierrc
|
5
|
+
"editor.insertSpaces": true,
|
6
|
+
"files.insertFinalNewline": true,
|
7
|
+
"files.trimFinalNewlines": true,
|
8
|
+
"files.trimTrailingWhitespace": true
|
9
|
+
}
|
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Luca Ban - Mesqueeb
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
# is What? 🙉
|
2
|
+
|
3
|
+
Very simple & small JS type check functions. It's fully TypeScript supported!
|
4
|
+
|
5
|
+
```
|
6
|
+
npm i is-what
|
7
|
+
```
|
8
|
+
|
9
|
+
Or for deno available at: `"deno.land/x/is_what"`
|
10
|
+
|
11
|
+
## Motivation
|
12
|
+
|
13
|
+
I built is-what because the existing solutions were all too complex or too poorly built.
|
14
|
+
|
15
|
+
I 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
|
+
|
21
|
+
And that's exactly what `is-what` is! (what a great wordplay 😃)
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
is-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:
|
29
|
+
import { isString, isDate, isPlainObject } from 'is-what'
|
30
|
+
```
|
31
|
+
|
32
|
+
1. First I'll go over the simple functions available. Only `isNumber` and `isDate` have special treatment.
|
33
|
+
2. After that I'll talk about working with Objects (plain objects vs class instances etc.).
|
34
|
+
3. Lastly I'll talk about TypeScript implementation
|
35
|
+
|
36
|
+
### Simple type check functions
|
37
|
+
|
38
|
+
```js
|
39
|
+
// basics
|
40
|
+
isBoolean(true) // true
|
41
|
+
isBoolean(false) // true
|
42
|
+
isUndefined(undefined) // true
|
43
|
+
isNull(null) // true
|
44
|
+
|
45
|
+
// strings
|
46
|
+
isString('') // true
|
47
|
+
isEmptyString('') // true
|
48
|
+
isFullString('') // false
|
49
|
+
|
50
|
+
// numbers
|
51
|
+
isNumber(0) // true
|
52
|
+
isNumber('0') // false
|
53
|
+
isNumber(NaN) // false *
|
54
|
+
isPositiveNumber(1) // true
|
55
|
+
isNegativeNumber(-1) // true
|
56
|
+
// * see below for special NaN use cases!
|
57
|
+
|
58
|
+
// arrays
|
59
|
+
isArray([]) // true
|
60
|
+
isEmptyArray([]) // true
|
61
|
+
isFullArray([1]) // true
|
62
|
+
|
63
|
+
// objects
|
64
|
+
isPlainObject({}) // true *
|
65
|
+
isEmptyObject({}) // true
|
66
|
+
isFullObject({ a: 1 }) // true
|
67
|
+
// * see below for special object (& class instance) use cases!
|
68
|
+
|
69
|
+
// functions
|
70
|
+
isFunction(function () {}) // true
|
71
|
+
isFunction(() => {}) // true
|
72
|
+
|
73
|
+
// dates
|
74
|
+
isDate(new Date()) // true
|
75
|
+
isDate(new Date('invalid date')) // false
|
76
|
+
|
77
|
+
// maps & sets
|
78
|
+
isMap(new Map()) // true
|
79
|
+
isSet(new Set()) // true
|
80
|
+
isWeakMap(new WeakMap()) // true
|
81
|
+
isWeakSet(new WeakSet()) // true
|
82
|
+
|
83
|
+
// others
|
84
|
+
isRegExp(/\s/gi) // true
|
85
|
+
isSymbol(Symbol()) // true
|
86
|
+
isBlob(new Blob()) // true
|
87
|
+
isFile(new File([''], '', { type: 'text/html' })) // true
|
88
|
+
isError(new Error('')) // true
|
89
|
+
isPromise(new Promise((resolve) => {})) // true
|
90
|
+
|
91
|
+
// primitives
|
92
|
+
isPrimitive('') // 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)
|
102
|
+
typeof NaN === 'number' // true
|
103
|
+
// 🤔 ("not a number" is a "number"...)
|
104
|
+
|
105
|
+
// 2)
|
106
|
+
isNaN('1') // false
|
107
|
+
// 🤔 the string '1' is not-"not a number"... so it's a number??
|
108
|
+
|
109
|
+
// 3)
|
110
|
+
isNaN('one') // true
|
111
|
+
// 🤔 'one' is NaN but `NaN === 'one'` is false...
|
112
|
+
```
|
113
|
+
|
114
|
+
With is-what the way we treat NaN makes a little bit more sense:
|
115
|
+
|
116
|
+
```js
|
117
|
+
import { isNumber, isNaNValue } from 'is-what'
|
118
|
+
|
119
|
+
// 1)
|
120
|
+
isNumber(NaN) // false!
|
121
|
+
// let's not treat NaN as a number
|
122
|
+
|
123
|
+
// 2)
|
124
|
+
isNaNValue('1') // false
|
125
|
+
// if it's not NaN, it's not NaN!!
|
126
|
+
|
127
|
+
// 3)
|
128
|
+
isNaNValue('one') // false
|
129
|
+
// if it's not NaN, it's not NaN!!
|
130
|
+
|
131
|
+
isNaNValue(NaN) // true
|
132
|
+
```
|
133
|
+
|
134
|
+
### isPlainObject vs isAnyObject
|
135
|
+
|
136
|
+
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:
|
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
|
143
|
+
const plainObject = { hello: 'I am a good old object.' }
|
144
|
+
|
145
|
+
// define a special object
|
146
|
+
class SpecialObject {
|
147
|
+
constructor(somethingSpecial) {
|
148
|
+
this.speciality = somethingSpecial
|
149
|
+
}
|
150
|
+
}
|
151
|
+
const specialObject = new SpecialObject('I am a special object! I am a class instance!!!')
|
152
|
+
|
153
|
+
// check the plain object
|
154
|
+
isPlainObject(plainObject) // returns true
|
155
|
+
isAnyObject(plainObject) // returns true
|
156
|
+
getType(plainObject) // returns 'Object'
|
157
|
+
|
158
|
+
// check the special object
|
159
|
+
isPlainObject(specialObject) // returns false !!!!!!!!!
|
160
|
+
isAnyObject(specialObject) // returns true
|
161
|
+
getType(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
|
+
|
168
|
+
You can check for specific types with `getType` and `isType`:
|
169
|
+
|
170
|
+
```js
|
171
|
+
import { getType, isType } from 'is-what'
|
172
|
+
|
173
|
+
getType('') // returns 'String'
|
174
|
+
// pass a Type as second param:
|
175
|
+
isType('', String) // returns true
|
176
|
+
```
|
177
|
+
|
178
|
+
## TypeScript
|
179
|
+
|
180
|
+
is-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
|
183
|
+
function 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:
|
189
|
+
function 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
|
201
|
+
function isPlainObject(payload: any): payload is { [key: string]: any }
|
202
|
+
function isAnyObject(payload: any): payload is { [key: string]: any }
|
203
|
+
// The reason to return `{[key: string]: any}` is to be able to do
|
204
|
+
if (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
|
+
|
210
|
+
If you want more control over what kind of interface/type is casted when checking for objects.
|
211
|
+
|
212
|
+
To cast to a specific type while checking for `isAnyObject`, can use `isObjectLike<T>`:
|
213
|
+
|
214
|
+
```ts
|
215
|
+
import { isObjectLike } from 'is-what'
|
216
|
+
|
217
|
+
const payload = { name: 'Mesqueeb' } // current type: `{ name: string }`
|
218
|
+
|
219
|
+
// Without casting:
|
220
|
+
if (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
|
227
|
+
if (isObjectLike<{ name: string }>(payload)) {
|
228
|
+
// in here `payload` is casted to: `{ name: string }`
|
229
|
+
}
|
230
|
+
```
|
231
|
+
|
232
|
+
Please 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
|
237
|
+
function 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
|
+
|
254
|
+
It's litterally just these functions:
|
255
|
+
|
256
|
+
```js
|
257
|
+
function getType(payload) {
|
258
|
+
return Object.prototype.toString.call(payload).slice(8, -1)
|
259
|
+
}
|
260
|
+
function isUndefined(payload) {
|
261
|
+
return getType(payload) === 'Undefined'
|
262
|
+
}
|
263
|
+
function isString(payload) {
|
264
|
+
return getType(payload) === 'String'
|
265
|
+
}
|
266
|
+
function isAnyObject(payload) {
|
267
|
+
return getType(payload) === 'Object'
|
268
|
+
}
|
269
|
+
// etc...
|
270
|
+
```
|
271
|
+
|
272
|
+
See the full source code [here](https://github.com/mesqueeb/is-what/blob/master/src/index.ts).
|
package/build.js
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
/* eslint-disable */
|
2
|
+
|
3
|
+
// npm i -D rollup rollup-plugin-typescript2 typescript
|
4
|
+
import typescript from 'rollup-plugin-typescript2'
|
5
|
+
|
6
|
+
// ------------------------------------------------------------------------------------------
|
7
|
+
// formats
|
8
|
+
// ------------------------------------------------------------------------------------------
|
9
|
+
// amd – Asynchronous Module Definition, used with module loaders like RequireJS
|
10
|
+
// cjs – CommonJS, suitable for Node and Browserify/Webpack
|
11
|
+
// esm – Keep the bundle as an ES module file
|
12
|
+
// iife – A self-executing function, suitable for inclusion as a <script> tag. (If you want to create a bundle for your application, you probably want to use this, because it leads to smaller file sizes.)
|
13
|
+
// umd – Universal Module Definition, works as amd, cjs and iife all in one
|
14
|
+
// system – Native format of the SystemJS loader
|
15
|
+
|
16
|
+
// ------------------------------------------------------------------------------------------
|
17
|
+
// setup
|
18
|
+
// ------------------------------------------------------------------------------------------
|
19
|
+
const pkg = require('./package.json')
|
20
|
+
const name = pkg.name
|
21
|
+
const className = name.replace(/(^\w|-\w)/g, (c) => c.replace('-', '').toUpperCase())
|
22
|
+
|
23
|
+
export default [
|
24
|
+
{
|
25
|
+
input: 'src/index.ts',
|
26
|
+
output: [
|
27
|
+
{
|
28
|
+
file: 'dist/index.js',
|
29
|
+
format: 'esm',
|
30
|
+
sourcemap: false,
|
31
|
+
name: className,
|
32
|
+
exports: 'named',
|
33
|
+
},
|
34
|
+
],
|
35
|
+
plugins: [
|
36
|
+
typescript({ useTsconfigDeclarationDir: true, tsconfigOverride: { exclude: ['test/**/*'] } }),
|
37
|
+
],
|
38
|
+
external: Object.keys(pkg.dependencies || []),
|
39
|
+
},
|
40
|
+
]
|