1 | # is
|
2 |
|
3 | > Type check values
|
4 |
|
5 | For example, `is.string('🦄') //=> true`
|
6 |
|
7 | <img src="header.gif" width="182" align="right">
|
8 |
|
9 | ## Highlights
|
10 |
|
11 | - Written in TypeScript
|
12 | - [Extensive use of type guards](#type-guards)
|
13 | - [Supports type assertions](#type-assertions)
|
14 | - [Aware of generic type parameters](#generic-type-parameters) (use with caution)
|
15 | - Actively maintained
|
16 | - ![Millions of downloads per week](https://img.shields.io/npm/dw/@sindresorhus/is)
|
17 |
|
18 | ## Install
|
19 |
|
20 | ```sh
|
21 | npm install @sindresorhus/is
|
22 | ```
|
23 |
|
24 | ## Usage
|
25 |
|
26 | ```js
|
27 | import is from '@sindresorhus/is';
|
28 |
|
29 | is('🦄');
|
30 | //=> 'string'
|
31 |
|
32 | is(new Map());
|
33 | //=> 'Map'
|
34 |
|
35 | is.number(6);
|
36 | //=> true
|
37 | ```
|
38 |
|
39 | [Assertions](#type-assertions) perform the same type checks, but throw an error if the type does not match.
|
40 |
|
41 | ```js
|
42 | import {assert} from '@sindresorhus/is';
|
43 |
|
44 | assert.string(2);
|
45 | //=> Error: Expected value which is `string`, received value of type `number`.
|
46 | ```
|
47 |
|
48 | Assertions (except `assertAll` and `assertAny`) also support an optional custom error message.
|
49 |
|
50 | ```js
|
51 | import {assert} from '@sindresorhus/is';
|
52 |
|
53 | assert.nonEmptyString(process.env.API_URL, 'The API_URL environment variable is required.');
|
54 | //=> Error: The API_URL environment variable is required.
|
55 | ```
|
56 |
|
57 | And with TypeScript:
|
58 |
|
59 | ```ts
|
60 | import {assert} from '@sindresorhus/is';
|
61 |
|
62 | assert.string(foo);
|
63 | // `foo` is now typed as a `string`.
|
64 | ```
|
65 |
|
66 | ### Named exports
|
67 |
|
68 | Named exports allow tooling to perform tree-shaking, potentially reducing bundle size by including only code from the methods that are used.
|
69 |
|
70 | Every method listed below is available as a named export. Each method is prefixed by either `is` or `assert` depending on usage.
|
71 |
|
72 | For example:
|
73 |
|
74 | ```js
|
75 | import {assertNull, isUndefined} from '@sindresorhus/is';
|
76 | ```
|
77 |
|
78 | ## API
|
79 |
|
80 | ### is(value)
|
81 |
|
82 | Returns the type of `value`.
|
83 |
|
84 | Primitives are lowercase and object types are camelcase.
|
85 |
|
86 | Example:
|
87 |
|
88 | - `'undefined'`
|
89 | - `'null'`
|
90 | - `'string'`
|
91 | - `'symbol'`
|
92 | - `'Array'`
|
93 | - `'Function'`
|
94 | - `'Object'`
|
95 |
|
96 | This method is also exported as `detect`. You can import it like this:
|
97 |
|
98 | ```js
|
99 | import {detect} from '@sindresorhus/is';
|
100 | ```
|
101 |
|
102 | Note: It will throw an error if you try to feed it object-wrapped primitives, as that's a bad practice. For example `new String('foo')`.
|
103 |
|
104 | ### is.{method}
|
105 |
|
106 | All the below methods accept a value and return a boolean for whether the value is of the desired type.
|
107 |
|
108 | #### Primitives
|
109 |
|
110 | ##### .undefined(value)
|
111 | ##### .null(value)
|
112 |
|
113 | ##### .string(value)
|
114 | ##### .number(value)
|
115 |
|
116 | Note: `is.number(NaN)` returns `false`. This intentionally deviates from `typeof` behavior to increase user-friendliness of `is` type checks.
|
117 |
|
118 | ##### .boolean(value)
|
119 | ##### .symbol(value)
|
120 | ##### .bigint(value)
|
121 |
|
122 | #### Built-in types
|
123 |
|
124 | ##### .array(value, assertion?)
|
125 |
|
126 | Returns true if `value` is an array and all of its items match the assertion (if provided).
|
127 |
|
128 | ```js
|
129 | is.array(value); // Validate `value` is an array.
|
130 | is.array(value, is.number); // Validate `value` is an array and all of its items are numbers.
|
131 | ```
|
132 |
|
133 | ##### .function(value)
|
134 |
|
135 | ##### .buffer(value)
|
136 | ##### .blob(value)
|
137 | ##### .object(value)
|
138 |
|
139 | Keep in mind that [functions are objects too](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions).
|
140 |
|
141 | ##### .numericString(value)
|
142 |
|
143 | Returns `true` for a string that represents a number satisfying `is.number`, for example, `'42'` and `'-8.3'`.
|
144 |
|
145 | Note: `'NaN'` returns `false`, but `'Infinity'` and `'-Infinity'` return `true`.
|
146 |
|
147 | ##### .regExp(value)
|
148 | ##### .date(value)
|
149 | ##### .error(value)
|
150 | ##### .nativePromise(value)
|
151 | ##### .promise(value)
|
152 |
|
153 | Returns `true` for any object with a `.then()` and `.catch()` method. Prefer this one over `.nativePromise()` as you usually want to allow userland promise implementations too.
|
154 |
|
155 | ##### .generator(value)
|
156 |
|
157 | Returns `true` for any object that implements its own `.next()` and `.throw()` methods and has a function definition for `Symbol.iterator`.
|
158 |
|
159 | ##### .generatorFunction(value)
|
160 |
|
161 | ##### .asyncFunction(value)
|
162 |
|
163 | Returns `true` for any `async` function that can be called with the `await` operator.
|
164 |
|
165 | ```js
|
166 | is.asyncFunction(async () => {});
|
167 | //=> true
|
168 |
|
169 | is.asyncFunction(() => {});
|
170 | //=> false
|
171 | ```
|
172 |
|
173 | ##### .asyncGenerator(value)
|
174 |
|
175 | ```js
|
176 | is.asyncGenerator(
|
177 | (async function * () {
|
178 | yield 4;
|
179 | })()
|
180 | );
|
181 | //=> true
|
182 |
|
183 | is.asyncGenerator(
|
184 | (function * () {
|
185 | yield 4;
|
186 | })()
|
187 | );
|
188 | //=> false
|
189 | ```
|
190 |
|
191 | ##### .asyncGeneratorFunction(value)
|
192 |
|
193 | ```js
|
194 | is.asyncGeneratorFunction(async function * () {
|
195 | yield 4;
|
196 | });
|
197 | //=> true
|
198 |
|
199 | is.asyncGeneratorFunction(function * () {
|
200 | yield 4;
|
201 | });
|
202 | //=> false
|
203 | ```
|
204 |
|
205 | ##### .boundFunction(value)
|
206 |
|
207 | Returns `true` for any `bound` function.
|
208 |
|
209 | ```js
|
210 | is.boundFunction(() => {});
|
211 | //=> true
|
212 |
|
213 | is.boundFunction(function () {}.bind(null));
|
214 | //=> true
|
215 |
|
216 | is.boundFunction(function () {});
|
217 | //=> false
|
218 | ```
|
219 |
|
220 | ##### .map(value)
|
221 | ##### .set(value)
|
222 | ##### .weakMap(value)
|
223 | ##### .weakSet(value)
|
224 | ##### .weakRef(value)
|
225 |
|
226 | #### Typed arrays
|
227 |
|
228 | ##### .int8Array(value)
|
229 | ##### .uint8Array(value)
|
230 | ##### .uint8ClampedArray(value)
|
231 | ##### .int16Array(value)
|
232 | ##### .uint16Array(value)
|
233 | ##### .int32Array(value)
|
234 | ##### .uint32Array(value)
|
235 | ##### .float32Array(value)
|
236 | ##### .float64Array(value)
|
237 | ##### .bigInt64Array(value)
|
238 | ##### .bigUint64Array(value)
|
239 |
|
240 | #### Structured data
|
241 |
|
242 | ##### .arrayBuffer(value)
|
243 | ##### .sharedArrayBuffer(value)
|
244 | ##### .dataView(value)
|
245 |
|
246 | ##### .enumCase(value, enum)
|
247 |
|
248 | TypeScript-only. Returns `true` if `value` is a member of `enum`.
|
249 |
|
250 | ```ts
|
251 | enum Direction {
|
252 | Ascending = 'ascending',
|
253 | Descending = 'descending'
|
254 | }
|
255 |
|
256 | is.enumCase('ascending', Direction);
|
257 | //=> true
|
258 |
|
259 | is.enumCase('other', Direction);
|
260 | //=> false
|
261 | ```
|
262 |
|
263 | #### Emptiness
|
264 |
|
265 | ##### .emptyString(value)
|
266 |
|
267 | Returns `true` if the value is a `string` and the `.length` is 0.
|
268 |
|
269 | ##### .emptyStringOrWhitespace(value)
|
270 |
|
271 | Returns `true` if `is.emptyString(value)` or if it's a `string` that is all whitespace.
|
272 |
|
273 | ##### .nonEmptyString(value)
|
274 |
|
275 | Returns `true` if the value is a `string` and the `.length` is more than 0.
|
276 |
|
277 | ##### .nonEmptyStringAndNotWhitespace(value)
|
278 |
|
279 | Returns `true` if the value is a `string` that is not empty and not whitespace.
|
280 |
|
281 | ```js
|
282 | const values = ['property1', '', null, 'property2', ' ', undefined];
|
283 |
|
284 | values.filter(is.nonEmptyStringAndNotWhitespace);
|
285 | //=> ['property1', 'property2']
|
286 | ```
|
287 |
|
288 | ##### .emptyArray(value)
|
289 |
|
290 | Returns `true` if the value is an `Array` and the `.length` is 0.
|
291 |
|
292 | ##### .nonEmptyArray(value)
|
293 |
|
294 | Returns `true` if the value is an `Array` and the `.length` is more than 0.
|
295 |
|
296 | ##### .emptyObject(value)
|
297 |
|
298 | Returns `true` if the value is an `Object` and `Object.keys(value).length` is 0.
|
299 |
|
300 | Please note that `Object.keys` returns only own enumerable properties. Hence something like this can happen:
|
301 |
|
302 | ```js
|
303 | const object1 = {};
|
304 |
|
305 | Object.defineProperty(object1, 'property1', {
|
306 | value: 42,
|
307 | writable: true,
|
308 | enumerable: false,
|
309 | configurable: true
|
310 | });
|
311 |
|
312 | is.emptyObject(object1);
|
313 | //=> true
|
314 | ```
|
315 |
|
316 | ##### .nonEmptyObject(value)
|
317 |
|
318 | Returns `true` if the value is an `Object` and `Object.keys(value).length` is more than 0.
|
319 |
|
320 | ##### .emptySet(value)
|
321 |
|
322 | Returns `true` if the value is a `Set` and the `.size` is 0.
|
323 |
|
324 | ##### .nonEmptySet(Value)
|
325 |
|
326 | Returns `true` if the value is a `Set` and the `.size` is more than 0.
|
327 |
|
328 | ##### .emptyMap(value)
|
329 |
|
330 | Returns `true` if the value is a `Map` and the `.size` is 0.
|
331 |
|
332 | ##### .nonEmptyMap(value)
|
333 |
|
334 | Returns `true` if the value is a `Map` and the `.size` is more than 0.
|
335 |
|
336 | #### Miscellaneous
|
337 |
|
338 | ##### .directInstanceOf(value, class)
|
339 |
|
340 | Returns `true` if `value` is a direct instance of `class`.
|
341 |
|
342 | ```js
|
343 | is.directInstanceOf(new Error(), Error);
|
344 | //=> true
|
345 |
|
346 | class UnicornError extends Error {}
|
347 |
|
348 | is.directInstanceOf(new UnicornError(), Error);
|
349 | //=> false
|
350 | ```
|
351 |
|
352 | ##### .urlInstance(value)
|
353 |
|
354 | Returns `true` if `value` is an instance of the [`URL` class](https://developer.mozilla.org/en-US/docs/Web/API/URL).
|
355 |
|
356 | ```js
|
357 | const url = new URL('https://example.com');
|
358 |
|
359 | is.urlInstance(url);
|
360 | //=> true
|
361 | ```
|
362 |
|
363 | ##### .urlString(value)
|
364 |
|
365 | Returns `true` if `value` is a URL string.
|
366 |
|
367 | Note: this only does basic checking using the [`URL` class](https://developer.mozilla.org/en-US/docs/Web/API/URL) constructor.
|
368 |
|
369 | ```js
|
370 | const url = 'https://example.com';
|
371 |
|
372 | is.urlString(url);
|
373 | //=> true
|
374 |
|
375 | is.urlString(new URL(url));
|
376 | //=> false
|
377 | ```
|
378 |
|
379 | ##### .truthy(value)
|
380 |
|
381 | Returns `true` for all values that evaluate to true in a boolean context:
|
382 |
|
383 | ```js
|
384 | is.truthy('🦄');
|
385 | //=> true
|
386 |
|
387 | is.truthy(undefined);
|
388 | //=> false
|
389 | ```
|
390 |
|
391 | ##### .falsy(value)
|
392 |
|
393 | Returns `true` if `value` is one of: `false`, `0`, `''`, `null`, `undefined`, `NaN`.
|
394 |
|
395 | ##### .nan(value)
|
396 | ##### .nullOrUndefined(value)
|
397 | ##### .primitive(value)
|
398 |
|
399 | JavaScript primitives are as follows:
|
400 |
|
401 | - `null`
|
402 | - `undefined`
|
403 | - `string`
|
404 | - `number`
|
405 | - `boolean`
|
406 | - `symbol`
|
407 | - `bigint`
|
408 |
|
409 | ##### .integer(value)
|
410 |
|
411 | ##### .safeInteger(value)
|
412 |
|
413 | Returns `true` if `value` is a [safe integer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger).
|
414 |
|
415 | ##### .plainObject(value)
|
416 |
|
417 | An object is plain if it's created by either `{}`, `new Object()`, or `Object.create(null)`.
|
418 |
|
419 | ##### .iterable(value)
|
420 | ##### .asyncIterable(value)
|
421 | ##### .class(value)
|
422 |
|
423 | Returns `true` if the value is a class constructor.
|
424 |
|
425 | ##### .typedArray(value)
|
426 |
|
427 | ##### .arrayLike(value)
|
428 |
|
429 | A `value` is array-like if it is not a function and has a `value.length` that is a safe integer greater than or equal to 0.
|
430 |
|
431 | ```js
|
432 | is.arrayLike(document.forms);
|
433 | //=> true
|
434 |
|
435 | function foo() {
|
436 | is.arrayLike(arguments);
|
437 | //=> true
|
438 | }
|
439 | foo();
|
440 | ```
|
441 |
|
442 | ##### .tupleLike(value, guards)
|
443 |
|
444 | A `value` is tuple-like if it matches the provided `guards` array both in `.length` and in types.
|
445 |
|
446 | ```js
|
447 | is.tupleLike([1], [is.number]);
|
448 | //=> true
|
449 | ```
|
450 |
|
451 | ```js
|
452 | function foo() {
|
453 | const tuple = [1, '2', true];
|
454 | if (is.tupleLike(tuple, [is.number, is.string, is.boolean])) {
|
455 | tuple // [number, string, boolean]
|
456 | }
|
457 | }
|
458 |
|
459 | foo();
|
460 | ```
|
461 |
|
462 | ##### .positiveNumber(value)
|
463 |
|
464 | Check if `value` is a number and is more than 0.
|
465 |
|
466 | ##### .negativeNumber(value)
|
467 |
|
468 | Check if `value` is a number and is less than 0.
|
469 |
|
470 | ##### .inRange(value, range)
|
471 |
|
472 | Check if `value` (number) is in the given `range`. The range is an array of two values, lower bound and upper bound, in no specific order.
|
473 |
|
474 | ```js
|
475 | is.inRange(3, [0, 5]);
|
476 | is.inRange(3, [5, 0]);
|
477 | is.inRange(0, [-2, 2]);
|
478 | ```
|
479 |
|
480 | ##### .inRange(value, upperBound)
|
481 |
|
482 | Check if `value` (number) is in the range of `0` to `upperBound`.
|
483 |
|
484 | ```js
|
485 | is.inRange(3, 10);
|
486 | ```
|
487 |
|
488 | ##### .htmlElement(value)
|
489 |
|
490 | Returns `true` if `value` is an [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement).
|
491 |
|
492 | ##### .nodeStream(value)
|
493 |
|
494 | Returns `true` if `value` is a Node.js [stream](https://nodejs.org/api/stream.html).
|
495 |
|
496 | ```js
|
497 | import fs from 'node:fs';
|
498 |
|
499 | is.nodeStream(fs.createReadStream('unicorn.png'));
|
500 | //=> true
|
501 | ```
|
502 |
|
503 | ##### .observable(value)
|
504 |
|
505 | Returns `true` if `value` is an `Observable`.
|
506 |
|
507 | ```js
|
508 | import {Observable} from 'rxjs';
|
509 |
|
510 | is.observable(new Observable());
|
511 | //=> true
|
512 | ```
|
513 |
|
514 | ##### .infinite(value)
|
515 |
|
516 | Check if `value` is `Infinity` or `-Infinity`.
|
517 |
|
518 | ##### .evenInteger(value)
|
519 |
|
520 | Returns `true` if `value` is an even integer.
|
521 |
|
522 | ##### .oddInteger(value)
|
523 |
|
524 | Returns `true` if `value` is an odd integer.
|
525 |
|
526 | ##### .propertyKey(value)
|
527 |
|
528 | Returns `true` if `value` can be used as an object property key (either `string`, `number`, or `symbol`).
|
529 |
|
530 | ##### .formData(value)
|
531 |
|
532 | Returns `true` if `value` is an instance of the [`FormData` class](https://developer.mozilla.org/en-US/docs/Web/API/FormData).
|
533 |
|
534 | ```js
|
535 | const data = new FormData();
|
536 |
|
537 | is.formData(data);
|
538 | //=> true
|
539 | ```
|
540 |
|
541 | ##### .urlSearchParams(value)
|
542 |
|
543 | Returns `true` if `value` is an instance of the [`URLSearchParams` class](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams).
|
544 |
|
545 | ```js
|
546 | const searchParams = new URLSearchParams();
|
547 |
|
548 | is.urlSearchParams(searchParams);
|
549 | //=> true
|
550 | ```
|
551 |
|
552 | ##### .any(predicate | predicate[], ...values)
|
553 |
|
554 | Using a single `predicate` argument, returns `true` if **any** of the input `values` returns true in the `predicate`:
|
555 |
|
556 | ```js
|
557 | is.any(is.string, {}, true, '🦄');
|
558 | //=> true
|
559 |
|
560 | is.any(is.boolean, 'unicorns', [], new Map());
|
561 | //=> false
|
562 | ```
|
563 |
|
564 | Using an array of `predicate[]`, returns `true` if **any** of the input `values` returns true for **any** of the `predicates` provided in an array:
|
565 |
|
566 | ```js
|
567 | is.any([is.string, is.number], {}, true, '🦄');
|
568 | //=> true
|
569 |
|
570 | is.any([is.boolean, is.number], 'unicorns', [], new Map());
|
571 | //=> false
|
572 | ```
|
573 |
|
574 | ##### .all(predicate, ...values)
|
575 |
|
576 | Returns `true` if **all** of the input `values` returns true in the `predicate`:
|
577 |
|
578 | ```js
|
579 | is.all(is.object, {}, new Map(), new Set());
|
580 | //=> true
|
581 |
|
582 | is.all(is.string, '🦄', [], 'unicorns');
|
583 | //=> false
|
584 | ```
|
585 |
|
586 | ##### .validDate(value)
|
587 |
|
588 | Returns `true` if the value is a valid date.
|
589 |
|
590 | All [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date) objects have an internal timestamp value which is the number of milliseconds since the [Unix epoch](https://developer.mozilla.org/en-US/docs/Glossary/Unix_time). When a new `Date` is constructed with bad inputs, no error is thrown. Instead, a new `Date` object is returned. But the internal timestamp value is set to `NaN`, which is an `'Invalid Date'`. Bad inputs can be an non-parsable date string, a non-numeric value or a number that is outside of the expected range for a date value.
|
591 |
|
592 | ```js
|
593 | const valid = new Date('2000-01-01');
|
594 |
|
595 | is.date(valid);
|
596 | //=> true
|
597 | valid.getTime();
|
598 | //=> 946684800000
|
599 | valid.toUTCString();
|
600 | //=> 'Sat, 01 Jan 2000 00:00:00 GMT'
|
601 | is.validDate(valid);
|
602 | //=> true
|
603 |
|
604 | const invalid = new Date('Not a parsable date string');
|
605 |
|
606 | is.date(invalid);
|
607 | //=> true
|
608 | invalid.getTime();
|
609 | //=> NaN
|
610 | invalid.toUTCString();
|
611 | //=> 'Invalid Date'
|
612 | is.validDate(invalid);
|
613 | //=> false
|
614 | ```
|
615 |
|
616 | ##### .validLength(value)
|
617 |
|
618 | Returns `true` if the value is a safe integer that is greater than or equal to zero.
|
619 |
|
620 | This can be useful to confirm that a value is a valid count of something, ie. 0 or more.
|
621 |
|
622 | ##### .whitespaceString(value)
|
623 |
|
624 | Returns `true` if the value is a string with only whitespace characters.
|
625 |
|
626 | ## Type guards
|
627 |
|
628 | When using `is` together with TypeScript, [type guards](http://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types) are being used extensively to infer the correct type inside if-else statements.
|
629 |
|
630 | ```ts
|
631 | import is from '@sindresorhus/is';
|
632 |
|
633 | const padLeft = (value: string, padding: string | number) => {
|
634 | if (is.number(padding)) {
|
635 | // `padding` is typed as `number`
|
636 | return Array(padding + 1).join(' ') + value;
|
637 | }
|
638 |
|
639 | if (is.string(padding)) {
|
640 | // `padding` is typed as `string`
|
641 | return padding + value;
|
642 | }
|
643 |
|
644 | throw new TypeError(`Expected 'padding' to be of type 'string' or 'number', got '${is(padding)}'.`);
|
645 | }
|
646 |
|
647 | padLeft('🦄', 3);
|
648 | //=> ' 🦄'
|
649 |
|
650 | padLeft('🦄', '🌈');
|
651 | //=> '🌈🦄'
|
652 | ```
|
653 |
|
654 | ## Type assertions
|
655 |
|
656 | The type guards are also available as [type assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions), which throw an error for unexpected types. It is a convenient one-line version of the often repetitive "if-not-expected-type-throw" pattern.
|
657 |
|
658 | ```ts
|
659 | import {assert} from '@sindresorhus/is';
|
660 |
|
661 | const handleMovieRatingApiResponse = (response: unknown) => {
|
662 | assert.plainObject(response);
|
663 | // `response` is now typed as a plain `object` with `unknown` properties.
|
664 |
|
665 | assert.number(response.rating);
|
666 | // `response.rating` is now typed as a `number`.
|
667 |
|
668 | assert.string(response.title);
|
669 | // `response.title` is now typed as a `string`.
|
670 |
|
671 | return `${response.title} (${response.rating * 10})`;
|
672 | };
|
673 |
|
674 | handleMovieRatingApiResponse({rating: 0.87, title: 'The Matrix'});
|
675 | //=> 'The Matrix (8.7)'
|
676 |
|
677 | // This throws an error.
|
678 | handleMovieRatingApiResponse({rating: '🦄'});
|
679 | ```
|
680 |
|
681 | ## Generic type parameters
|
682 |
|
683 | The type guards and type assertions are aware of [generic type parameters](https://www.typescriptlang.org/docs/handbook/generics.html), such as `Promise<T>` and `Map<Key, Value>`. The default is `unknown` for most cases, since `is` cannot check them at runtime. If the generic type is known at compile-time, either implicitly (inferred) or explicitly (provided), `is` propagates the type so it can be used later.
|
684 |
|
685 | Use generic type parameters with caution. They are only checked by the TypeScript compiler, and not checked by `is` at runtime. This can lead to unexpected behavior, where the generic type is _assumed_ at compile-time, but actually is something completely different at runtime. It is best to use `unknown` (default) and type-check the value of the generic type parameter at runtime with `is` or `assert`.
|
686 |
|
687 | ```ts
|
688 | import {assert} from '@sindresorhus/is';
|
689 |
|
690 | async function badNumberAssumption(input: unknown) {
|
691 | // Bad assumption about the generic type parameter fools the compile-time type system.
|
692 | assert.promise<number>(input);
|
693 | // `input` is a `Promise` but only assumed to be `Promise<number>`.
|
694 |
|
695 | const resolved = await input;
|
696 | // `resolved` is typed as `number` but was not actually checked at runtime.
|
697 |
|
698 | // Multiplication will return NaN if the input promise did not actually contain a number.
|
699 | return 2 * resolved;
|
700 | }
|
701 |
|
702 | async function goodNumberAssertion(input: unknown) {
|
703 | assert.promise(input);
|
704 | // `input` is typed as `Promise<unknown>`
|
705 |
|
706 | const resolved = await input;
|
707 | // `resolved` is typed as `unknown`
|
708 |
|
709 | assert.number(resolved);
|
710 | // `resolved` is typed as `number`
|
711 |
|
712 | // Uses runtime checks so only numbers will reach the multiplication.
|
713 | return 2 * resolved;
|
714 | }
|
715 |
|
716 | badNumberAssumption(Promise.resolve('An unexpected string'));
|
717 | //=> NaN
|
718 |
|
719 | // This correctly throws an error because of the unexpected string value.
|
720 | goodNumberAssertion(Promise.resolve('An unexpected string'));
|
721 | ```
|
722 |
|
723 | ## FAQ
|
724 |
|
725 | ### Why yet another type checking module?
|
726 |
|
727 | There are hundreds of type checking modules on npm, unfortunately, I couldn't find any that fit my needs:
|
728 |
|
729 | - Includes both type methods and ability to get the type
|
730 | - Types of primitives returned as lowercase and object types as camelcase
|
731 | - Covers all built-ins
|
732 | - Unsurprising behavior
|
733 | - Well-maintained
|
734 | - Comprehensive test suite
|
735 |
|
736 | For the ones I found, pick 3 of these.
|
737 |
|
738 | The most common mistakes I noticed in these modules was using `instanceof` for type checking, forgetting that functions are objects, and omitting `symbol` as a primitive.
|
739 |
|
740 | ### Why not just use `instanceof` instead of this package?
|
741 |
|
742 | `instanceof` does not work correctly for all types and it does not work across [realms](https://stackoverflow.com/a/49832343/64949). Examples of realms are iframes, windows, web workers, and the `vm` module in Node.js.
|
743 |
|
744 | ## Related
|
745 |
|
746 | - [ow](https://github.com/sindresorhus/ow) - Function argument validation for humans
|
747 | - [is-stream](https://github.com/sindresorhus/is-stream) - Check if something is a Node.js stream
|
748 | - [is-observable](https://github.com/sindresorhus/is-observable) - Check if a value is an Observable
|
749 | - [file-type](https://github.com/sindresorhus/file-type) - Detect the file type of a Buffer/Uint8Array
|
750 | - [is-ip](https://github.com/sindresorhus/is-ip) - Check if a string is an IP address
|
751 | - [is-array-sorted](https://github.com/sindresorhus/is-array-sorted) - Check if an Array is sorted
|
752 | - [is-error-constructor](https://github.com/sindresorhus/is-error-constructor) - Check if a value is an error constructor
|
753 | - [is-empty-iterable](https://github.com/sindresorhus/is-empty-iterable) - Check if an Iterable is empty
|
754 | - [is-blob](https://github.com/sindresorhus/is-blob) - Check if a value is a Blob - File-like object of immutable, raw data
|
755 | - [has-emoji](https://github.com/sindresorhus/has-emoji) - Check whether a string has any emoji
|
756 |
|
757 | ## Maintainers
|
758 |
|
759 | - [Sindre Sorhus](https://github.com/sindresorhus)
|
760 | - [Giora Guttsait](https://github.com/gioragutt)
|
761 | - [Brandon Smith](https://github.com/brandon93s)
|