Implement named exports (#191)

This commit is contained in:
Bjorn Stromberg 2023-08-07 08:50:03 +08:00 committed by GitHub
parent bd5dfda993
commit 5044c91273
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 1282 additions and 409 deletions

View file

@ -54,6 +54,18 @@ assert.string(foo);
// `foo` is now typed as a `string`.
```
### Named exports
Named exports allow tooling to perform tree-shaking, potentially reducing bundle size by including only code from the methods that are used.
Every method listed below is available as a named export. Each method is prefixed by either `is` or `assert` depending on usage.
For example:
```js
import {assertNull, isUndefined} from '@sindresorhus/is';
```
## API
### is(value)
@ -72,19 +84,23 @@ Example:
- `'Function'`
- `'Object'`
This method is also exported as `detect`. You can import it like this:
```js
import {detect} from '@sindresorhus/is';
```
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')`.
### is.{method}
All the below methods accept a value and returns a boolean for whether the value is of the desired type.
All the below methods accept a value and return a boolean for whether the value is of the desired type.
#### Primitives
##### .undefined(value)
##### .null(value)
**Note:** TypeScript users must use `.null_()` because of a TypeScript naming limitation.
##### .string(value)
##### .number(value)
@ -107,8 +123,6 @@ is.array(value, is.number); // Validate `value` is an array and all of its items
##### .function(value)
**Note:** TypeScript users must use `.function_()` because of a TypeScript naming limitation.
##### .buffer(value)
##### .blob(value)
##### .object(value)
@ -373,7 +387,15 @@ Returns `true` if `value` is one of: `false`, `0`, `''`, `null`, `undefined`, `N
##### .nullOrUndefined(value)
##### .primitive(value)
JavaScript primitives are as follows: `null`, `undefined`, `string`, `number`, `boolean`, `symbol`.
JavaScript primitives are as follows:
- `null`
- `undefined`
- `string`
- `number`
- `boolean`
- `symbol`
- `bigint`
##### .integer(value)
@ -391,8 +413,6 @@ An object is plain if it's created by either `{}`, `new Object()`, or `Object.cr
Returns `true` for instances created by a class.
**Note:** TypeScript users must use `.class_()` because of a TypeScript naming limitation.
##### .typedArray(value)
##### .arrayLike(value)
@ -458,7 +478,7 @@ is.inRange(3, 10);
##### .domElement(value)
Returns `true` if `value` is a DOM Element.
Returns `true` if `value` is an [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement).
##### .nodeStream(value)
@ -554,6 +574,16 @@ is.all(is.string, '🦄', [], 'unicorns');
//=> false
```
##### .validLength(value)
Returns `true` if the value is a safe integer that is greater than or equal to zero.
This can be useful to confirm that a value is a valid count of something, ie. 0 or more.
##### .whitespaceString(value)
Returns `true` if the value is a string with only whitespace characters.
## Type guards
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.

File diff suppressed because it is too large Load diff

View file

@ -62,3 +62,14 @@ export type WeakRef<T extends object> = { // eslint-disable-line @typescript-esl
readonly [Symbol.toStringTag]: 'WeakRef';
deref(): T | undefined;
};
export type ArrayLike<T> = {
readonly [index: number]: T;
readonly length: number;
};
export type NodeStream = {
pipe<T extends NodeJS.WritableStream>(destination: T, options?: {end?: boolean}): T;
} & NodeJS.EventEmitter;
export type Predicate = (value: unknown) => boolean;

View file

@ -61,7 +61,7 @@ const types = new Map<string, Test>([
typename: 'undefined',
}],
['null', {
is: is.null_,
is: is.null,
assert: assert.null_,
fixtures: [
null,
@ -161,7 +161,7 @@ const types = new Map<string, Test>([
typeDescription: 'empty array',
}],
['function', {
is: is.function_,
is: is.function,
assert: assert.function_,
fixtures: [
function foo() {}, // eslint-disable-line func-names
@ -838,7 +838,7 @@ test('is.asyncFunction', t => {
const fixture = async () => {};
if (is.asyncFunction(fixture)) {
t.true(is.function_(fixture().then));
t.true(is.function(fixture().then));
t.notThrows(() => {
assert.function_(fixture().then);
@ -857,7 +857,7 @@ test('is.asyncGenerator', t => {
yield 4;
})();
if (is.asyncGenerator(fixture)) {
t.true(is.function_(fixture.next));
t.true(is.function(fixture.next));
}
});
@ -873,7 +873,7 @@ test('is.asyncGeneratorFunction', t => {
};
if (is.asyncGeneratorFunction(fixture)) {
t.true(is.function_(fixture().next));
t.true(is.function(fixture().next));
}
});
@ -1365,7 +1365,7 @@ test('is.class', t => {
];
for (const classDeclaration of classDeclarations) {
t.true(is.class_(classDeclaration));
t.true(is.class(classDeclaration));
t.notThrows(() => {
assert.class_(classDeclaration);
@ -1456,7 +1456,7 @@ test('is.tupleLike', t => {
t.false(is.tupleLike('unicorn', [is.string]));
t.false(is.tupleLike({}, []));
t.false(is.tupleLike(() => {}, [is.function_]));
t.false(is.tupleLike(() => {}, [is.function]));
t.false(is.tupleLike(new Map(), [is.map]));
(function () {
@ -1476,7 +1476,7 @@ test('is.tupleLike', t => {
assert.tupleLike({}, [is.object]);
});
t.throws(() => {
assert.tupleLike(() => {}, [is.function_]);
assert.tupleLike(() => {}, [is.function]);
});
t.throws(() => {
assert.tupleLike(new Map(), [is.map]);
@ -1496,7 +1496,7 @@ test('is.tupleLike', t => {
{
const tuple = [{isTest: true}, '1', true, null];
if (is.tupleLike(tuple, [is.nonEmptyObject, is.string, is.boolean, is.null_])) {
if (is.tupleLike(tuple, [is.nonEmptyObject, is.string, is.boolean, is.null])) {
const value = tuple[0];
expectTypeOf(value).toEqualTypeOf<Record<string | number | symbol, unknown>>();
}
@ -1505,7 +1505,7 @@ test('is.tupleLike', t => {
{
const tuple = [1, '1', true, null, undefined];
if (is.tupleLike(tuple, [is.number, is.string, is.boolean, is.undefined, is.null_])) {
if (is.tupleLike(tuple, [is.number, is.string, is.boolean, is.undefined, is.null])) {
const numericValue = tuple[0];
const stringValue = tuple[1];
const booleanValue = tuple[2];
@ -1540,14 +1540,17 @@ test('is.inRange', t => {
t.false(is.inRange(-3, -2));
t.throws(() => {
// @ts-expect-error invalid argument
is.inRange(0, []);
});
t.throws(() => {
// @ts-expect-error invalid argument
is.inRange(0, [5]);
});
t.throws(() => {
// @ts-expect-error invalid argument
is.inRange(0, [1, 2, 3]);
});
@ -1604,14 +1607,17 @@ test('is.inRange', t => {
});
t.throws(() => {
// @ts-expect-error invalid argument
assert.inRange(0, []);
});
t.throws(() => {
// @ts-expect-error invalid argument
assert.inRange(0, [5]);
});
t.throws(() => {
// @ts-expect-error invalid argument
assert.inRange(0, [1, 2, 3]);
});
});
@ -2072,6 +2078,30 @@ test('is.urlSearchParams', t => {
});
});
test('is.validLength', t => {
t.true(is.validLength(1));
t.true(is.validLength(0));
t.false(is.validLength(-1));
t.false(is.validLength(0.1));
t.notThrows(() => {
assert.validLength(1);
});
t.throws(() => {
assert.validLength(-1);
});
});
test('is.whitespaceString', t => {
t.true(is.whitespaceString(' '));
t.true(is.whitespaceString(' '));
t.true(is.whitespaceString('   '));
t.true(is.whitespaceString('\u3000'));
t.true(is.whitespaceString(' '));
t.false(is.whitespaceString(''));
t.false(is.whitespaceString('-'));
t.false(is.whitespaceString(' hi '));
});
test('assert', t => {
// Contrived test showing that TypeScript acknowledges the type assertion in `assert.number()`.
// Real--world usage includes asserting user input, but here we use a random number/string generator.