forked from orbit-oss/is
Add negativeInteger, nonNegativeInteger, arrayOf, and oneOf predicates
This commit is contained in:
parent
63be5c0c19
commit
54fc09406a
3 changed files with 173 additions and 2 deletions
30
readme.md
30
readme.md
|
|
@ -130,6 +130,17 @@ is.array(value); // Validate `value` is an array.
|
||||||
is.array(value, is.number); // Validate `value` is an array and all of its items are numbers.
|
is.array(value, is.number); // Validate `value` is an array and all of its items are numbers.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### .arrayOf(predicate)
|
||||||
|
|
||||||
|
Returns a type guard that checks if `value` is an array where every item matches the predicate. Useful for composing with other methods.
|
||||||
|
|
||||||
|
```js
|
||||||
|
const isStringArray = is.arrayOf(is.string);
|
||||||
|
|
||||||
|
isStringArray(['a', 'b']); //=> true
|
||||||
|
isStringArray(['a', 1]); //=> false
|
||||||
|
```
|
||||||
|
|
||||||
##### .function(value)
|
##### .function(value)
|
||||||
|
|
||||||
##### .buffer(value)
|
##### .buffer(value)
|
||||||
|
|
@ -483,6 +494,14 @@ Check if `value` is a number and is 0 or more.
|
||||||
|
|
||||||
Check if `value` is an integer and is more than 0.
|
Check if `value` is an integer and is more than 0.
|
||||||
|
|
||||||
|
##### .negativeInteger(value)
|
||||||
|
|
||||||
|
Check if `value` is an integer and is less than 0.
|
||||||
|
|
||||||
|
##### .nonNegativeInteger(value)
|
||||||
|
|
||||||
|
Check if `value` is an integer and is 0 or more.
|
||||||
|
|
||||||
##### .inRange(value, range)
|
##### .inRange(value, range)
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
@ -661,6 +680,17 @@ is.optional(123, is.string);
|
||||||
//=> false
|
//=> false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### .oneOf(values)
|
||||||
|
|
||||||
|
Returns a type guard that checks if `value` is one of the given `values`. Best used with `as const` for precise type narrowing.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const isDirection = is.oneOf(['north', 'south', 'east', 'west'] as const);
|
||||||
|
|
||||||
|
isDirection('north'); //=> true
|
||||||
|
isDirection('up'); //=> false
|
||||||
|
```
|
||||||
|
|
||||||
##### .validDate(value)
|
##### .validDate(value)
|
||||||
|
|
||||||
Returns `true` if the value is a valid date.
|
Returns `true` if the value is a valid date.
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,12 @@ const assertionTypeDescriptions = [
|
||||||
'non-empty map',
|
'non-empty map',
|
||||||
'PropertyKey',
|
'PropertyKey',
|
||||||
'even integer',
|
'even integer',
|
||||||
|
'finite number',
|
||||||
|
'negative integer',
|
||||||
|
'non-negative integer',
|
||||||
|
'non-negative number',
|
||||||
'odd integer',
|
'odd integer',
|
||||||
|
'positive integer',
|
||||||
'T',
|
'T',
|
||||||
'in range',
|
'in range',
|
||||||
'predicate returns truthy for any value',
|
'predicate returns truthy for any value',
|
||||||
|
|
@ -240,6 +245,7 @@ const is = Object.assign(
|
||||||
array: isArray,
|
array: isArray,
|
||||||
arrayBuffer: isArrayBuffer,
|
arrayBuffer: isArrayBuffer,
|
||||||
arrayLike: isArrayLike,
|
arrayLike: isArrayLike,
|
||||||
|
arrayOf: isArrayOf,
|
||||||
asyncFunction: isAsyncFunction,
|
asyncFunction: isAsyncFunction,
|
||||||
asyncGenerator: isAsyncGenerator,
|
asyncGenerator: isAsyncGenerator,
|
||||||
asyncGeneratorFunction: isAsyncGeneratorFunction,
|
asyncGeneratorFunction: isAsyncGeneratorFunction,
|
||||||
|
|
@ -284,6 +290,7 @@ const is = Object.assign(
|
||||||
map: isMap,
|
map: isMap,
|
||||||
nan: isNan,
|
nan: isNan,
|
||||||
nativePromise: isNativePromise,
|
nativePromise: isNativePromise,
|
||||||
|
negativeInteger: isNegativeInteger,
|
||||||
negativeNumber: isNegativeNumber,
|
negativeNumber: isNegativeNumber,
|
||||||
nodeStream: isNodeStream,
|
nodeStream: isNodeStream,
|
||||||
nonEmptyArray: isNonEmptyArray,
|
nonEmptyArray: isNonEmptyArray,
|
||||||
|
|
@ -292,6 +299,7 @@ const is = Object.assign(
|
||||||
nonEmptySet: isNonEmptySet,
|
nonEmptySet: isNonEmptySet,
|
||||||
nonEmptyString: isNonEmptyString,
|
nonEmptyString: isNonEmptyString,
|
||||||
nonEmptyStringAndNotWhitespace: isNonEmptyStringAndNotWhitespace,
|
nonEmptyStringAndNotWhitespace: isNonEmptyStringAndNotWhitespace,
|
||||||
|
nonNegativeInteger: isNonNegativeInteger,
|
||||||
nonNegativeNumber: isNonNegativeNumber,
|
nonNegativeNumber: isNonNegativeNumber,
|
||||||
null: isNull,
|
null: isNull,
|
||||||
nullOrUndefined: isNullOrUndefined,
|
nullOrUndefined: isNullOrUndefined,
|
||||||
|
|
@ -300,6 +308,7 @@ const is = Object.assign(
|
||||||
object: isObject,
|
object: isObject,
|
||||||
observable: isObservable,
|
observable: isObservable,
|
||||||
oddInteger: isOddInteger,
|
oddInteger: isOddInteger,
|
||||||
|
oneOf: isOneOf,
|
||||||
plainObject: isPlainObject,
|
plainObject: isPlainObject,
|
||||||
positiveInteger: isPositiveInteger,
|
positiveInteger: isPositiveInteger,
|
||||||
positiveNumber: isPositiveNumber,
|
positiveNumber: isPositiveNumber,
|
||||||
|
|
@ -435,6 +444,10 @@ export function isArrayLike<T = unknown>(value: unknown): value is ArrayLike<T>
|
||||||
return !isNullOrUndefined(value) && !isFunction(value) && isValidLength((value as ArrayLike<T>).length);
|
return !isNullOrUndefined(value) && !isFunction(value) && isValidLength((value as ArrayLike<T>).length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isArrayOf<T>(predicate: (value: unknown) => value is T): (value: unknown) => value is T[] {
|
||||||
|
return (value: unknown): value is T[] => isArray(value) && value.every(element => predicate(element));
|
||||||
|
}
|
||||||
|
|
||||||
export function isAsyncFunction<T = unknown>(value: unknown): value is ((...arguments_: any[]) => Promise<T>) {
|
export function isAsyncFunction<T = unknown>(value: unknown): value is ((...arguments_: any[]) => Promise<T>) {
|
||||||
return getObjectType(value) === 'AsyncFunction';
|
return getObjectType(value) === 'AsyncFunction';
|
||||||
}
|
}
|
||||||
|
|
@ -648,6 +661,10 @@ export function isNativePromise<T = unknown>(value: unknown): value is Promise<T
|
||||||
return getObjectType(value) === 'Promise';
|
return getObjectType(value) === 'Promise';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isNegativeInteger(value: unknown): value is number {
|
||||||
|
return isInteger(value) && value < 0;
|
||||||
|
}
|
||||||
|
|
||||||
export function isNegativeNumber(value: unknown): value is number {
|
export function isNegativeNumber(value: unknown): value is number {
|
||||||
return isNumber(value) && value < 0;
|
return isNumber(value) && value < 0;
|
||||||
}
|
}
|
||||||
|
|
@ -684,6 +701,10 @@ export function isNonEmptyStringAndNotWhitespace(value: unknown): value is NonEm
|
||||||
return isString(value) && !isEmptyStringOrWhitespace(value);
|
return isString(value) && !isEmptyStringOrWhitespace(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isNonNegativeInteger(value: unknown): value is number {
|
||||||
|
return isInteger(value) && value >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
export function isNonNegativeNumber(value: unknown): value is number {
|
export function isNonNegativeNumber(value: unknown): value is number {
|
||||||
return isNumber(value) && value >= 0;
|
return isNumber(value) && value >= 0;
|
||||||
}
|
}
|
||||||
|
|
@ -733,6 +754,10 @@ export function isOddInteger(value: unknown): value is number {
|
||||||
return isAbsoluteModule2(1)(value);
|
return isAbsoluteModule2(1)(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isOneOf<T extends readonly unknown[]>(values: T): (value: unknown) => value is T[number] {
|
||||||
|
return (value: unknown): value is T[number] => values.includes(value as T[number]);
|
||||||
|
}
|
||||||
|
|
||||||
export function isPlainObject<Value = unknown>(value: unknown): value is Record<PropertyKey, Value> {
|
export function isPlainObject<Value = unknown>(value: unknown): value is Record<PropertyKey, Value> {
|
||||||
// From: https://github.com/sindresorhus/is-plain-obj/blob/main/index.js
|
// From: https://github.com/sindresorhus/is-plain-obj/blob/main/index.js
|
||||||
if (typeof value !== 'object' || value === null) {
|
if (typeof value !== 'object' || value === null) {
|
||||||
|
|
@ -925,7 +950,9 @@ type Assert = {
|
||||||
number: (value: unknown, message?: string) => asserts value is number;
|
number: (value: unknown, message?: string) => asserts value is number;
|
||||||
finiteNumber: (value: unknown, message?: string) => asserts value is number;
|
finiteNumber: (value: unknown, message?: string) => asserts value is number;
|
||||||
positiveNumber: (value: unknown, message?: string) => asserts value is number;
|
positiveNumber: (value: unknown, message?: string) => asserts value is number;
|
||||||
|
negativeInteger: (value: unknown, message?: string) => asserts value is number;
|
||||||
negativeNumber: (value: unknown, message?: string) => asserts value is number;
|
negativeNumber: (value: unknown, message?: string) => asserts value is number;
|
||||||
|
nonNegativeInteger: (value: unknown, message?: string) => asserts value is number;
|
||||||
nonNegativeNumber: (value: unknown, message?: string) => asserts value is number;
|
nonNegativeNumber: (value: unknown, message?: string) => asserts value is number;
|
||||||
positiveInteger: (value: unknown, message?: string) => asserts value is number;
|
positiveInteger: (value: unknown, message?: string) => asserts value is number;
|
||||||
bigint: (value: unknown, message?: string) => asserts value is bigint;
|
bigint: (value: unknown, message?: string) => asserts value is bigint;
|
||||||
|
|
@ -1086,6 +1113,7 @@ export const assert: Assert = {
|
||||||
map: assertMap,
|
map: assertMap,
|
||||||
nan: assertNan,
|
nan: assertNan,
|
||||||
nativePromise: assertNativePromise,
|
nativePromise: assertNativePromise,
|
||||||
|
negativeInteger: assertNegativeInteger,
|
||||||
negativeNumber: assertNegativeNumber,
|
negativeNumber: assertNegativeNumber,
|
||||||
nodeStream: assertNodeStream,
|
nodeStream: assertNodeStream,
|
||||||
nonEmptyArray: assertNonEmptyArray,
|
nonEmptyArray: assertNonEmptyArray,
|
||||||
|
|
@ -1094,6 +1122,7 @@ export const assert: Assert = {
|
||||||
nonEmptySet: assertNonEmptySet,
|
nonEmptySet: assertNonEmptySet,
|
||||||
nonEmptyString: assertNonEmptyString,
|
nonEmptyString: assertNonEmptyString,
|
||||||
nonEmptyStringAndNotWhitespace: assertNonEmptyStringAndNotWhitespace,
|
nonEmptyStringAndNotWhitespace: assertNonEmptyStringAndNotWhitespace,
|
||||||
|
nonNegativeInteger: assertNonNegativeInteger,
|
||||||
nonNegativeNumber: assertNonNegativeNumber,
|
nonNegativeNumber: assertNonNegativeNumber,
|
||||||
null: assertNull,
|
null: assertNull,
|
||||||
nullOrUndefined: assertNullOrUndefined,
|
nullOrUndefined: assertNullOrUndefined,
|
||||||
|
|
@ -1180,6 +1209,7 @@ const methodTypeMap = {
|
||||||
isMap: 'Map',
|
isMap: 'Map',
|
||||||
isNan: 'NaN',
|
isNan: 'NaN',
|
||||||
isNativePromise: 'native Promise',
|
isNativePromise: 'native Promise',
|
||||||
|
isNegativeInteger: 'negative integer',
|
||||||
isNegativeNumber: 'negative number',
|
isNegativeNumber: 'negative number',
|
||||||
isNodeStream: 'Node.js Stream',
|
isNodeStream: 'Node.js Stream',
|
||||||
isNonEmptyArray: 'non-empty array',
|
isNonEmptyArray: 'non-empty array',
|
||||||
|
|
@ -1188,6 +1218,7 @@ const methodTypeMap = {
|
||||||
isNonEmptySet: 'non-empty set',
|
isNonEmptySet: 'non-empty set',
|
||||||
isNonEmptyString: 'non-empty string',
|
isNonEmptyString: 'non-empty string',
|
||||||
isNonEmptyStringAndNotWhitespace: 'non-empty string and not whitespace',
|
isNonEmptyStringAndNotWhitespace: 'non-empty string and not whitespace',
|
||||||
|
isNonNegativeInteger: 'non-negative integer',
|
||||||
isNonNegativeNumber: 'non-negative number',
|
isNonNegativeNumber: 'non-negative number',
|
||||||
isNull: 'null',
|
isNull: 'null',
|
||||||
isNullOrUndefined: 'null or undefined',
|
isNullOrUndefined: 'null or undefined',
|
||||||
|
|
@ -1553,6 +1584,12 @@ export function assertNativePromise<T = unknown>(value: unknown, message?: strin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assertNegativeInteger(value: unknown, message?: string): asserts value is number {
|
||||||
|
if (!isNegativeInteger(value)) {
|
||||||
|
throw new TypeError(message ?? typeErrorMessage('negative integer', value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function assertNegativeNumber(value: unknown, message?: string): asserts value is number {
|
export function assertNegativeNumber(value: unknown, message?: string): asserts value is number {
|
||||||
if (!isNegativeNumber(value)) {
|
if (!isNegativeNumber(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('negative number', value));
|
throw new TypeError(message ?? typeErrorMessage('negative number', value));
|
||||||
|
|
@ -1601,6 +1638,12 @@ export function assertNonEmptyStringAndNotWhitespace(value: unknown, message?: s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assertNonNegativeInteger(value: unknown, message?: string): asserts value is number {
|
||||||
|
if (!isNonNegativeInteger(value)) {
|
||||||
|
throw new TypeError(message ?? typeErrorMessage('non-negative integer', value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function assertNonNegativeNumber(value: unknown, message?: string): asserts value is number {
|
export function assertNonNegativeNumber(value: unknown, message?: string): asserts value is number {
|
||||||
if (!isNonNegativeNumber(value)) {
|
if (!isNonNegativeNumber(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('non-negative number', value));
|
throw new TypeError(message ?? typeErrorMessage('non-negative number', value));
|
||||||
|
|
|
||||||
102
test/test.ts
102
test/test.ts
|
|
@ -666,6 +666,69 @@ test('is.positiveInteger', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('is.negativeInteger', () => {
|
||||||
|
assert.ok(is.negativeInteger(-1));
|
||||||
|
assert.ok(is.negativeInteger(-6));
|
||||||
|
assert.ok(is.negativeInteger(-100));
|
||||||
|
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.negativeInteger(-1);
|
||||||
|
});
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.negativeInteger(-6);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(is.negativeInteger(0), false);
|
||||||
|
assert.strictEqual(is.negativeInteger(-0), false); // -0 < 0 is false in JavaScript
|
||||||
|
assert.strictEqual(is.negativeInteger(1), false);
|
||||||
|
assert.strictEqual(is.negativeInteger(-1.5), false);
|
||||||
|
assert.strictEqual(is.negativeInteger(Number.NEGATIVE_INFINITY), false);
|
||||||
|
assert.strictEqual(is.negativeInteger(Number.NaN), false);
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
isAssert.negativeInteger(0);
|
||||||
|
});
|
||||||
|
assert.throws(() => {
|
||||||
|
isAssert.negativeInteger(1);
|
||||||
|
});
|
||||||
|
assert.throws(() => {
|
||||||
|
isAssert.negativeInteger(-1.5);
|
||||||
|
});
|
||||||
|
assert.throws(() => {
|
||||||
|
isAssert.negativeInteger(Number.NEGATIVE_INFINITY);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('is.nonNegativeInteger', () => {
|
||||||
|
assert.ok(is.nonNegativeInteger(0));
|
||||||
|
assert.ok(is.nonNegativeInteger(1));
|
||||||
|
assert.ok(is.nonNegativeInteger(100));
|
||||||
|
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.nonNegativeInteger(0);
|
||||||
|
});
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.nonNegativeInteger(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.ok(is.nonNegativeInteger(-0)); // -0 >= 0 is true in JavaScript
|
||||||
|
|
||||||
|
assert.strictEqual(is.nonNegativeInteger(-1), false);
|
||||||
|
assert.strictEqual(is.nonNegativeInteger(1.5), false);
|
||||||
|
assert.strictEqual(is.nonNegativeInteger(Number.POSITIVE_INFINITY), false);
|
||||||
|
assert.strictEqual(is.nonNegativeInteger(Number.NaN), false);
|
||||||
|
|
||||||
|
assert.throws(() => {
|
||||||
|
isAssert.nonNegativeInteger(-1);
|
||||||
|
});
|
||||||
|
assert.throws(() => {
|
||||||
|
isAssert.nonNegativeInteger(1.5);
|
||||||
|
});
|
||||||
|
assert.throws(() => {
|
||||||
|
isAssert.nonNegativeInteger(Number.POSITIVE_INFINITY);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('is.numericString supplemental', () => {
|
test('is.numericString supplemental', () => {
|
||||||
assert.strictEqual(is.numericString(''), false);
|
assert.strictEqual(is.numericString(''), false);
|
||||||
assert.strictEqual(is.numericString(' '), false);
|
assert.strictEqual(is.numericString(' '), false);
|
||||||
|
|
@ -713,6 +776,41 @@ test('is.array supplemental', () => {
|
||||||
}, /Expected numbers/v);
|
}, /Expected numbers/v);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('is.arrayOf', () => {
|
||||||
|
const isStringArray = is.arrayOf(is.string);
|
||||||
|
assert.ok(isStringArray(['a', 'b', 'c']));
|
||||||
|
assert.ok(isStringArray([]));
|
||||||
|
assert.strictEqual(isStringArray([1, 2, 3]), false);
|
||||||
|
assert.strictEqual(isStringArray(['a', 1]), false);
|
||||||
|
assert.strictEqual(isStringArray('not an array'), false);
|
||||||
|
assert.strictEqual(isStringArray(undefined), false);
|
||||||
|
|
||||||
|
const isNumberArray = is.arrayOf(is.number);
|
||||||
|
assert.ok(isNumberArray([1, 2, 3]));
|
||||||
|
assert.strictEqual(isNumberArray([1, '2']), false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('is.oneOf', () => {
|
||||||
|
const isDirection = is.oneOf(['north', 'south', 'east', 'west'] as const);
|
||||||
|
assert.ok(isDirection('north'));
|
||||||
|
assert.ok(isDirection('west'));
|
||||||
|
assert.strictEqual(isDirection('up'), false);
|
||||||
|
assert.strictEqual(isDirection(1), false);
|
||||||
|
assert.strictEqual(isDirection(undefined), false);
|
||||||
|
|
||||||
|
const isSmallNumber = is.oneOf([1, 2, 3] as const);
|
||||||
|
assert.ok(isSmallNumber(1));
|
||||||
|
assert.strictEqual(isSmallNumber(4), false);
|
||||||
|
|
||||||
|
// Empty values array always returns false
|
||||||
|
const isNever = is.oneOf([] as const);
|
||||||
|
assert.strictEqual(isNever('anything'), false);
|
||||||
|
|
||||||
|
// Array.includes uses SameValueZero, so NaN matches NaN (unlike ===)
|
||||||
|
const isNanValue = is.oneOf([Number.NaN] as const);
|
||||||
|
assert.ok(isNanValue(Number.NaN));
|
||||||
|
});
|
||||||
|
|
||||||
test('is.boundFunction supplemental', () => {
|
test('is.boundFunction supplemental', () => {
|
||||||
assert.strictEqual(is.boundFunction(function () {}), false); // eslint-disable-line prefer-arrow-callback
|
assert.strictEqual(is.boundFunction(function () {}), false); // eslint-disable-line prefer-arrow-callback
|
||||||
|
|
||||||
|
|
@ -1359,11 +1457,11 @@ test('is.inRange', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
is.inRange(5, [NaN, 10]);
|
is.inRange(5, [Number.NaN, 10]);
|
||||||
}, TypeError);
|
}, TypeError);
|
||||||
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
is.inRange(5, [0, NaN]);
|
is.inRange(5, [0, Number.NaN]);
|
||||||
}, TypeError);
|
}, TypeError);
|
||||||
|
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue