From 5044c912730c7f7480881b197c870b92cabbcaef Mon Sep 17 00:00:00 2001 From: Bjorn Stromberg Date: Mon, 7 Aug 2023 08:50:03 +0800 Subject: [PATCH] Implement named exports (#191) --- readme.md | 48 +- source/index.ts | 1582 +++++++++++++++++++++++++++++++++++------------ source/types.ts | 11 + test/test.ts | 50 +- 4 files changed, 1282 insertions(+), 409 deletions(-) diff --git a/readme.md b/readme.md index 40726c5..05d395b 100644 --- a/readme.md +++ b/readme.md @@ -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. diff --git a/source/index.ts b/source/index.ts index fe78aa7..0239440 100644 --- a/source/index.ts +++ b/source/index.ts @@ -1,5 +1,15 @@ import type {Buffer} from 'node:buffer'; -import type {Class, Falsy, TypedArray, ObservableLike, Primitive, WeakRef} from './types.js'; +import type { + ArrayLike, + Class, + Falsy, + NodeStream, + ObservableLike, + Predicate, + Primitive, + TypedArray, + WeakRef, +} from './types.js'; const typedArrayTypeNames = [ 'Int8Array', @@ -117,22 +127,18 @@ const assertionTypeDescriptions = [ 'in range', 'predicate returns truthy for any value', 'predicate returns truthy for all values', + 'valid length', + 'whitespace string', ...objectTypeNames, ...primitiveTypeNames, ] as const; export type AssertionTypeDescription = typeof assertionTypeDescriptions[number]; -// eslint-disable-next-line @typescript-eslint/ban-types -function isOfType(type: PrimitiveTypeName | 'function') { - return (value: unknown): value is T => typeof value === type; -} - -const {toString} = Object.prototype; const getObjectType = (value: unknown): ObjectTypeName | undefined => { - const objectTypeName = toString.call(value).slice(8, -1); + const objectTypeName = Object.prototype.toString.call(value).slice(8, -1); - if (/HTML\w+Element/.test(objectTypeName) && is.domElement(value)) { + if (/HTML\w+Element/.test(objectTypeName) && isDomElement(value)) { return 'HTMLElement'; } @@ -143,9 +149,7 @@ const getObjectType = (value: unknown): ObjectTypeName | undefined => { return undefined; }; -const isObjectOfType = (type: ObjectTypeName) => (value: unknown): value is T => getObjectType(value) === type; - -function is(value: unknown): TypeName { +function detect(value: unknown): TypeName { if (value === null) { return 'null'; } @@ -182,15 +186,15 @@ function is(value: unknown): TypeName { default: } - if (is.observable(value)) { + if (isObservable(value)) { return 'Observable'; } - if (is.array(value)) { + if (isArray(value)) { return 'Array'; } - if (is.buffer(value)) { + if (isBuffer(value)) { return 'Buffer'; } @@ -206,201 +210,276 @@ function is(value: unknown): TypeName { return 'Object'; } -is.undefined = isOfType('undefined'); +function hasPromiseApi(value: unknown): value is Promise { + return isFunction((value as Promise)?.then) && isFunction((value as Promise)?.catch); +} -is.string = isOfType('string'); +const is = Object.assign( + detect, + { + all: isAll, + any: isAny, + array: isArray, + arrayBuffer: isArrayBuffer, + arrayLike: isArrayLike, + asyncFunction: isAsyncFunction, + asyncGenerator: isAsyncGenerator, + asyncGeneratorFunction: isAsyncGeneratorFunction, + asyncIterable: isAsyncIterable, + bigint: isBigint, + bigInt64Array: isBigInt64Array, + bigUint64Array: isBigUint64Array, + blob: isBlob, + boolean: isBoolean, + boundFunction: isBoundFunction, + buffer: isBuffer, + class: isClass, + /** @deprecated Renamed to `class`. */ + class_: isClass, + dataView: isDataView, + date: isDate, + detect, + directInstanceOf: isDirectInstanceOf, + domElement: isDomElement, + emptyArray: isEmptyArray, + emptyMap: isEmptyMap, + emptyObject: isEmptyObject, + emptySet: isEmptySet, + emptyString: isEmptyString, + emptyStringOrWhitespace: isEmptyStringOrWhitespace, + enumCase: isEnumCase, + error: isError, + evenInteger: isEvenInteger, + falsy: isFalsy, + float32Array: isFloat32Array, + float64Array: isFloat64Array, + formData: isFormData, + function: isFunction, + /** @deprecated Renamed to `function`. */ + function_: isFunction, + generator: isGenerator, + generatorFunction: isGeneratorFunction, + infinite: isInfinite, + inRange: isInRange, + int16Array: isInt16Array, + int32Array: isInt32Array, + int8Array: isInt8Array, + integer: isInteger, + iterable: isIterable, + map: isMap, + nan: isNan, + nativePromise: isNativePromise, + negativeNumber: isNegativeNumber, + nodeStream: isNodeStream, + nonEmptyArray: isNonEmptyArray, + nonEmptyMap: isNonEmptyMap, + nonEmptyObject: isNonEmptyObject, + nonEmptySet: isNonEmptySet, + nonEmptyString: isNonEmptyString, + nonEmptyStringAndNotWhitespace: isNonEmptyStringAndNotWhitespace, + null: isNull, + /** @deprecated Renamed to `null`. */ + null_: isNull, + nullOrUndefined: isNullOrUndefined, + number: isNumber, + numericString: isNumericString, + object: isObject, + observable: isObservable, + oddInteger: isOddInteger, + plainObject: isPlainObject, + positiveNumber: isPositiveNumber, + primitive: isPrimitive, + promise: isPromise, + propertyKey: isPropertyKey, + regExp: isRegExp, + safeInteger: isSafeInteger, + set: isSet, + sharedArrayBuffer: isSharedArrayBuffer, + string: isString, + symbol: isSymbol, + truthy: isTruthy, + tupleLike: isTupleLike, + typedArray: isTypedArray, + uint16Array: isUint16Array, + uint32Array: isUint32Array, + uint8Array: isUint8Array, + uint8ClampedArray: isUint8ClampedArray, + undefined: isUndefined, + urlInstance: isUrlInstance, + urlSearchParams: isUrlSearchParams, + urlString: isUrlString, + validLength: isValidLength, + weakMap: isWeakMap, + weakRef: isWeakRef, + weakSet: isWeakSet, + whitespaceString: isWhitespaceString, + }, +); -const isNumberType = isOfType('number'); -is.number = (value: unknown): value is number => isNumberType(value) && !is.nan(value); +function isAbsoluteMod2(remainder: 0 | 1) { + return (value: unknown): value is number => isInteger(value) && Math.abs(value % 2) === remainder; +} -is.positiveNumber = (value: unknown): value is number => is.number(value) && value > 0; +export function isAll(predicate: Predicate, ...values: unknown[]): boolean { + return predicateOnArray(Array.prototype.every, predicate, values); +} -is.negativeNumber = (value: unknown): value is number => is.number(value) && value < 0; +export function isAny(predicate: Predicate | Predicate[], ...values: unknown[]): boolean { + const predicates = isArray(predicate) ? predicate : [predicate]; + return predicates.some(singlePredicate => + predicateOnArray(Array.prototype.some, singlePredicate, values), + ); +} -is.bigint = isOfType('bigint'); - -// eslint-disable-next-line @typescript-eslint/ban-types -is.function_ = isOfType('function'); - -// eslint-disable-next-line @typescript-eslint/ban-types -is.null_ = (value: unknown): value is null => value === null; - -is.class_ = (value: unknown): value is Class => is.function_(value) && value.toString().startsWith('class '); - -is.boolean = (value: unknown): value is boolean => value === true || value === false; - -is.symbol = isOfType('symbol'); - -is.numericString = (value: unknown): value is `${number}` => - is.string(value) && !is.emptyStringOrWhitespace(value) && !Number.isNaN(Number(value)); - -is.array = (value: unknown, assertion?: (value: T) => value is T): value is T[] => { +export function isArray(value: unknown, assertion?: (value: T) => value is T): value is T[] { if (!Array.isArray(value)) { return false; } - if (!is.function_(assertion)) { + if (!isFunction(assertion)) { return true; } // eslint-disable-next-line @typescript-eslint/no-unsafe-argument return value.every(element => assertion(element)); -}; +} -// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call -is.buffer = (value: unknown): value is Buffer => (value as any)?.constructor?.isBuffer?.(value) ?? false; +export function isArrayBuffer(value: unknown): value is ArrayBuffer { + return getObjectType(value) === 'ArrayBuffer'; +} -is.blob = (value: unknown): value is Blob => isObjectOfType('Blob')(value); +export function isArrayLike(value: unknown): value is ArrayLike { + return !isNullOrUndefined(value) && !isFunction(value) && isValidLength((value as ArrayLike).length); +} -is.nullOrUndefined = (value: unknown): value is null | undefined => is.null_(value) || is.undefined(value); // eslint-disable-line @typescript-eslint/ban-types +export function isAsyncFunction(value: unknown): value is ((...args: any[]) => Promise) { + return getObjectType(value) === 'AsyncFunction'; +} -is.object = (value: unknown): value is object => !is.null_(value) && (typeof value === 'object' || is.function_(value)); // eslint-disable-line @typescript-eslint/ban-types +export function isAsyncGenerator(value: unknown): value is AsyncGenerator { + return isAsyncIterable(value) && isFunction((value as AsyncGenerator).next) && isFunction((value as AsyncGenerator).throw); +} -is.iterable = (value: unknown): value is Iterable => is.function_((value as Iterable)?.[Symbol.iterator]); +export function isAsyncGeneratorFunction(value: unknown): value is ((...args: any[]) => Promise) { + return getObjectType(value) === 'AsyncGeneratorFunction'; +} -is.asyncIterable = (value: unknown): value is AsyncIterable => is.function_((value as AsyncIterable)?.[Symbol.asyncIterator]); +export function isAsyncIterable(value: unknown): value is AsyncIterable { + return isFunction((value as AsyncIterable)?.[Symbol.asyncIterator]); +} -is.generator = (value: unknown): value is Generator => is.iterable(value) && is.function_((value as Generator)?.next) && is.function_((value as Generator)?.throw); +export function isBigint(value: unknown): value is bigint { + return typeof value === 'bigint'; +} -is.asyncGenerator = (value: unknown): value is AsyncGenerator => is.asyncIterable(value) && is.function_((value as AsyncGenerator).next) && is.function_((value as AsyncGenerator).throw); +export function isBigInt64Array(value: unknown): value is BigInt64Array { + return getObjectType(value) === 'BigInt64Array'; +} -is.nativePromise = (value: unknown): value is Promise => - isObjectOfType>('Promise')(value); +export function isBigUint64Array(value: unknown): value is BigUint64Array { + return getObjectType(value) === 'BigUint64Array'; +} -const hasPromiseApi = (value: unknown): value is Promise => - is.function_((value as Promise)?.then) - && is.function_((value as Promise)?.catch); +export function isBlob(value: unknown): value is Blob { + return getObjectType(value) === 'Blob'; +} -is.promise = (value: unknown): value is Promise => is.nativePromise(value) || hasPromiseApi(value); - -is.generatorFunction = isObjectOfType('GeneratorFunction'); - -is.asyncGeneratorFunction = (value: unknown): value is ((...args: any[]) => Promise) => getObjectType(value) === 'AsyncGeneratorFunction'; - -is.asyncFunction = (value: unknown): value is ((...args: any[]) => Promise) => getObjectType(value) === 'AsyncFunction'; - -// eslint-disable-next-line no-prototype-builtins, @typescript-eslint/ban-types -is.boundFunction = (value: unknown): value is Function => is.function_(value) && !value.hasOwnProperty('prototype'); - -is.regExp = isObjectOfType('RegExp'); - -is.date = isObjectOfType('Date'); - -is.error = isObjectOfType('Error'); - -is.map = (value: unknown): value is Map => isObjectOfType>('Map')(value); - -is.set = (value: unknown): value is Set => isObjectOfType>('Set')(value); - -is.weakMap = (value: unknown): value is WeakMap => isObjectOfType>('WeakMap')(value); // eslint-disable-line @typescript-eslint/ban-types - -is.weakSet = (value: unknown): value is WeakSet => isObjectOfType>('WeakSet')(value); // eslint-disable-line @typescript-eslint/ban-types - -is.weakRef = (value: unknown): value is WeakRef => isObjectOfType>('WeakRef')(value); // eslint-disable-line @typescript-eslint/ban-types - -is.int8Array = isObjectOfType('Int8Array'); -is.uint8Array = isObjectOfType('Uint8Array'); -is.uint8ClampedArray = isObjectOfType('Uint8ClampedArray'); -is.int16Array = isObjectOfType('Int16Array'); -is.uint16Array = isObjectOfType('Uint16Array'); -is.int32Array = isObjectOfType('Int32Array'); -is.uint32Array = isObjectOfType('Uint32Array'); -is.float32Array = isObjectOfType('Float32Array'); -is.float64Array = isObjectOfType('Float64Array'); -is.bigInt64Array = isObjectOfType('BigInt64Array'); -is.bigUint64Array = isObjectOfType('BigUint64Array'); - -is.arrayBuffer = isObjectOfType('ArrayBuffer'); - -is.sharedArrayBuffer = isObjectOfType('SharedArrayBuffer'); - -is.dataView = isObjectOfType('DataView'); - -// eslint-disable-next-line @typescript-eslint/no-unsafe-argument -is.enumCase = (value: unknown, targetEnum: T): boolean => Object.values(targetEnum as any).includes(value as string); - -is.directInstanceOf = (instance: unknown, class_: Class): instance is T => Object.getPrototypeOf(instance) === class_.prototype; - -is.urlInstance = (value: unknown): value is URL => isObjectOfType('URL')(value); - -is.urlString = (value: unknown): value is string => { - if (!is.string(value)) { - return false; - } - - try { - new URL(value); // eslint-disable-line no-new - return true; - } catch { - return false; - } -}; - -// Example: `is.truthy = (value: unknown): value is (not false | not 0 | not '' | not undefined | not null) => Boolean(value);` -is.truthy = (value: T | Falsy): value is T => Boolean(value); // eslint-disable-line unicorn/prefer-native-coercion-functions - -// Example: `is.falsy = (value: unknown): value is (not true | 0 | '' | undefined | null) => Boolean(value);` -is.falsy = (value: unknown): value is Falsy => !value; - -is.nan = (value: unknown) => Number.isNaN(value as number); - -is.primitive = (value: unknown): value is Primitive => is.null_(value) || isPrimitiveTypeName(typeof value); - -is.integer = (value: unknown): value is number => Number.isInteger(value as number); - -is.safeInteger = (value: unknown): value is number => Number.isSafeInteger(value as number); - -is.plainObject = (value: unknown): value is Record => { - // From: https://github.com/sindresorhus/is-plain-obj/blob/main/index.js - if (typeof value !== 'object' || value === null) { - return false; - } - - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const prototype = Object.getPrototypeOf(value); - - return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value); -}; - -is.typedArray = (value: unknown): value is TypedArray => isTypedArrayName(getObjectType(value)); - -export type ArrayLike = { - readonly [index: number]: T; - readonly length: number; -}; - -const isValidLength = (value: unknown): value is number => is.safeInteger(value) && value >= 0; -is.arrayLike = (value: unknown): value is ArrayLike => !is.nullOrUndefined(value) && !is.function_(value) && isValidLength((value as ArrayLike).length); - -type TypeGuard = (value: unknown) => value is T; +export function isBoolean(value: unknown): value is boolean { + return value === true || value === false; +} // eslint-disable-next-line @typescript-eslint/ban-types -type ResolveTypesOfTypeGuardsTuple = - TypeGuardsOfT extends [TypeGuard, ...infer TOthers] - ? ResolveTypesOfTypeGuardsTuple - : TypeGuardsOfT extends undefined[] - ? ResultOfT - : never; +export function isBoundFunction(value: unknown): value is Function { + return isFunction(value) && !Object.prototype.hasOwnProperty.call(value, 'prototype'); +} -is.tupleLike = >>(value: unknown, guards: [...T]): value is ResolveTypesOfTypeGuardsTuple => { - if (is.array(guards) && is.array(value) && guards.length === value.length) { - return guards.every((guard, index) => guard(value[index])); - } +export function isBuffer(value: unknown): value is Buffer { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call + return (value as any)?.constructor?.isBuffer?.(value) ?? false; +} - return false; -}; +export function isClass(value: unknown): value is Class { + return isFunction(value) && value.toString().startsWith('class '); +} -is.inRange = (value: number, range: number | number[]): value is number => { - if (is.number(range)) { - return value >= Math.min(0, range) && value <= Math.max(range, 0); - } +export function isDataView(value: unknown): value is DataView { + return getObjectType(value) === 'DataView'; +} - if (is.array(range) && range.length === 2) { - return value >= Math.min(...range) && value <= Math.max(...range); - } +export function isDate(value: unknown): value is Date { + return getObjectType(value) === 'Date'; +} - throw new TypeError(`Invalid range: ${JSON.stringify(range)}`); -}; +export function isDirectInstanceOf(instance: unknown, class_: Class): instance is T { + return Object.getPrototypeOf(instance) === class_.prototype; +} + +export function isEmptyArray(value: unknown): value is never[] { + return isArray(value) && value.length === 0; +} + +export function isEmptyMap(value: unknown): value is Map { + return isMap(value) && value.size === 0; +} + +export function isEmptyObject(value: unknown): value is Record { + return isObject(value) && !isMap(value) && !isSet(value) && Object.keys(value).length === 0; +} + +export function isEmptySet(value: unknown): value is Set { + return isSet(value) && value.size === 0; +} + +export function isEmptyString(value: unknown): value is '' { + return isString(value) && value.length === 0; +} + +export function isEmptyStringOrWhitespace(value: unknown): value is string { + return isEmptyString(value) || isWhitespaceString(value); +} + +export function isEnumCase(value: unknown, targetEnum: T): boolean { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + return Object.values(targetEnum as any).includes(value as string); +} + +export function isError(value: unknown): value is Error { + return getObjectType(value) === 'Error'; +} + +export function isEvenInteger(value: unknown): value is number { + return isAbsoluteMod2(0)(value); +} + +// Example: `is.falsy = (value: unknown): value is (not true | 0 | '' | undefined | null) => Boolean(value);` +export function isFalsy(value: unknown): value is Falsy { + return !value; +} + +export function isFloat32Array(value: unknown): value is Float32Array { + return getObjectType(value) === 'Float32Array'; +} + +export function isFloat64Array(value: unknown): value is Float64Array { + return getObjectType(value) === 'Float64Array'; +} + +export function isFormData(value: unknown): value is FormData { + return getObjectType(value) === 'FormData'; +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function isFunction(value: unknown): value is Function { + return typeof value === 'function'; +} + +export function isGenerator(value: unknown): value is Generator { + return isIterable(value) && isFunction((value as Generator)?.next) && isFunction((value as Generator)?.throw); +} + +export function isGeneratorFunction(value: unknown): value is GeneratorFunction { + return getObjectType(value) === 'GeneratorFunction'; +} // eslint-disable-next-line @typescript-eslint/naming-convention const NODE_TYPE_ELEMENT = 1; @@ -414,13 +493,122 @@ const DOM_PROPERTIES_TO_CHECK: Array<(keyof HTMLElement)> = [ 'nodeValue', ]; -is.domElement = (value: unknown): value is HTMLElement => is.object(value) - && (value as HTMLElement).nodeType === NODE_TYPE_ELEMENT - && is.string((value as HTMLElement).nodeName) - && !is.plainObject(value) - && DOM_PROPERTIES_TO_CHECK.every(property => property in value); +export function isDomElement(value: unknown): value is HTMLElement { + return isObject(value) + && (value as HTMLElement).nodeType === NODE_TYPE_ELEMENT + && isString((value as HTMLElement).nodeName) + && !isPlainObject(value) + && DOM_PROPERTIES_TO_CHECK.every(property => property in value); +} -is.observable = (value: unknown): value is ObservableLike => { +export function isInfinite(value: unknown): value is number { + return value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY; +} + +export function isInRange(value: number, range: number | [number, number]): value is number { + if (isNumber(range)) { + return value >= Math.min(0, range) && value <= Math.max(range, 0); + } + + if (isArray(range) && range.length === 2) { + return value >= Math.min(...range) && value <= Math.max(...range); + } + + throw new TypeError(`Invalid range: ${JSON.stringify(range)}`); +} + +export function isInt16Array(value: unknown): value is Int16Array { + return getObjectType(value) === 'Int16Array'; +} + +export function isInt32Array(value: unknown): value is Int32Array { + return getObjectType(value) === 'Int32Array'; +} + +export function isInt8Array(value: unknown): value is Int8Array { + return getObjectType(value) === 'Int8Array'; +} + +export function isInteger(value: unknown): value is number { + return Number.isInteger(value); +} + +export function isIterable(value: unknown): value is Iterable { + return isFunction((value as Iterable)?.[Symbol.iterator]); +} + +export function isMap(value: unknown): value is Map { + return getObjectType(value) === 'Map'; +} + +export function isNan(value: unknown) { + return Number.isNaN(value); +} + +export function isNativePromise(value: unknown): value is Promise { + return getObjectType(value) === 'Promise'; +} + +export function isNegativeNumber(value: unknown): value is number { + return isNumber(value) && value < 0; +} + +export function isNodeStream(value: unknown): value is NodeStream { + return isObject(value) && isFunction((value as NodeStream).pipe) && !isObservable(value); +} + +export function isNonEmptyArray(value: T | Item[]): value is [Item, ...Item[]] { + return isArray(value) && value.length > 0; +} + +export function isNonEmptyMap(value: unknown): value is Map { + return isMap(value) && value.size > 0; +} + +// TODO: Use `not` operator here to remove `Map` and `Set` from type guard: +// - https://github.com/Microsoft/TypeScript/pull/29317 +export function isNonEmptyObject(value: unknown): value is Record { + return isObject(value) && !isMap(value) && !isSet(value) && Object.keys(value).length > 0; +} + +export function isNonEmptySet(value: unknown): value is Set { + return isSet(value) && value.size > 0; +} + +// TODO: Use `not ''` when the `not` operator is available. +export function isNonEmptyString(value: unknown): value is string { + return isString(value) && value.length > 0; +} + +// TODO: Use `not ''` when the `not` operator is available. +export function isNonEmptyStringAndNotWhitespace(value: unknown): value is string { + return isString(value) && !isEmptyStringOrWhitespace(value); +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function isNull(value: unknown): value is null { + return value === null; +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function isNullOrUndefined(value: unknown): value is null | undefined { + return isNull(value) || isUndefined(value); +} + +export function isNumber(value: unknown): value is number { + return typeof value === 'number' && !Number.isNaN(value); +} + +export function isNumericString(value: unknown): value is `${number}` { + return isString(value) && !isEmptyStringOrWhitespace(value) && !Number.isNaN(Number(value)); +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function isObject(value: unknown): value is object { + return !isNull(value) && (typeof value === 'object' || isFunction(value)); +} + +export function isObservable(value: unknown): value is ObservableLike { if (!value) { return false; } @@ -436,66 +624,162 @@ is.observable = (value: unknown): value is ObservableLike => { } return false; -}; +} -export type NodeStream = { - pipe(destination: T, options?: {end?: boolean}): T; -} & NodeJS.EventEmitter; +export function isOddInteger(value: unknown): value is number { + return isAbsoluteMod2(1)(value); +} -is.nodeStream = (value: unknown): value is NodeStream => is.object(value) && is.function_((value as NodeStream).pipe) && !is.observable(value); +export function isPlainObject(value: unknown): value is Record { + // From: https://github.com/sindresorhus/is-plain-obj/blob/main/index.js + if (typeof value !== 'object' || value === null) { + return false; + } -is.infinite = (value: unknown): value is number => value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY; + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const prototype = Object.getPrototypeOf(value); -const isAbsoluteMod2 = (remainder: number) => (value: number): value is number => is.integer(value) && Math.abs(value % 2) === remainder; -is.evenInteger = isAbsoluteMod2(0); -is.oddInteger = isAbsoluteMod2(1); + return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value); +} -is.emptyArray = (value: unknown): value is never[] => is.array(value) && value.length === 0; +export function isPositiveNumber(value: unknown): value is number { + return isNumber(value) && value > 0; +} -is.nonEmptyArray = (value: T | Item[]): value is [Item, ...Item[]] => is.array(value) && value.length > 0; +export function isPrimitive(value: unknown): value is Primitive { + return isNull(value) || isPrimitiveTypeName(typeof value); +} -is.emptyString = (value: unknown): value is '' => is.string(value) && value.length === 0; - -const isWhiteSpaceString = (value: unknown): value is string => is.string(value) && !/\S/.test(value); -is.emptyStringOrWhitespace = (value: unknown): value is string => is.emptyString(value) || isWhiteSpaceString(value); - -// TODO: Use `not ''` when the `not` operator is available. -is.nonEmptyString = (value: unknown): value is string => is.string(value) && value.length > 0; - -// TODO: Use `not ''` when the `not` operator is available. -is.nonEmptyStringAndNotWhitespace = (value: unknown): value is string => is.string(value) && !is.emptyStringOrWhitespace(value); - -// eslint-disable-next-line unicorn/no-array-callback-reference -is.emptyObject = (value: unknown): value is Record => is.object(value) && !is.map(value) && !is.set(value) && Object.keys(value).length === 0; - -// TODO: Use `not` operator here to remove `Map` and `Set` from type guard: -// - https://github.com/Microsoft/TypeScript/pull/29317 -// eslint-disable-next-line unicorn/no-array-callback-reference -is.nonEmptyObject = (value: unknown): value is Record => is.object(value) && !is.map(value) && !is.set(value) && Object.keys(value).length > 0; - -is.emptySet = (value: unknown): value is Set => is.set(value) && value.size === 0; - -is.nonEmptySet = (value: unknown): value is Set => is.set(value) && value.size > 0; - -// eslint-disable-next-line unicorn/no-array-callback-reference -is.emptyMap = (value: unknown): value is Map => is.map(value) && value.size === 0; - -// eslint-disable-next-line unicorn/no-array-callback-reference -is.nonEmptyMap = (value: unknown): value is Map => is.map(value) && value.size > 0; +export function isPromise(value: unknown): value is Promise { + return isNativePromise(value) || hasPromiseApi(value); +} // `PropertyKey` is any value that can be used as an object key (string, number, or symbol) -is.propertyKey = (value: unknown): value is PropertyKey => is.any([is.string, is.number, is.symbol], value); +export function isPropertyKey(value: unknown): value is PropertyKey { + return isAny([isString, isNumber, isSymbol], value); +} -is.formData = (value: unknown): value is FormData => isObjectOfType('FormData')(value); +export function isRegExp(value: unknown): value is RegExp { + return getObjectType(value) === 'RegExp'; +} -is.urlSearchParams = (value: unknown): value is URLSearchParams => isObjectOfType('URLSearchParams')(value); +export function isSafeInteger(value: unknown): value is number { + return Number.isSafeInteger(value); +} -export type Predicate = (value: unknown) => boolean; +export function isSet(value: unknown): value is Set { + return getObjectType(value) === 'Set'; +} + +export function isSharedArrayBuffer(value: unknown): value is SharedArrayBuffer { + return getObjectType(value) === 'SharedArrayBuffer'; +} + +export function isString(value: unknown): value is string { + return typeof value === 'string'; +} + +export function isSymbol(value: unknown): value is symbol { + return typeof value === 'symbol'; +} + +// Example: `is.truthy = (value: unknown): value is (not false | not 0 | not '' | not undefined | not null) => Boolean(value);` +// eslint-disable-next-line unicorn/prefer-native-coercion-functions +export function isTruthy(value: T | Falsy): value is T { + return Boolean(value); +} + +type TypeGuard = (value: unknown) => value is T; + +// eslint-disable-next-line @typescript-eslint/ban-types +type ResolveTypesOfTypeGuardsTuple = + TypeGuardsOfT extends [TypeGuard, ...infer TOthers] + ? ResolveTypesOfTypeGuardsTuple + : TypeGuardsOfT extends undefined[] + ? ResultOfT + : never; + +export function isTupleLike>>(value: unknown, guards: [...T]): value is ResolveTypesOfTypeGuardsTuple { + if (isArray(guards) && isArray(value) && guards.length === value.length) { + return guards.every((guard, index) => guard(value[index])); + } + + return false; +} + +export function isTypedArray(value: unknown): value is TypedArray { + return isTypedArrayName(getObjectType(value)); +} + +export function isUint16Array(value: unknown): value is Uint16Array { + return getObjectType(value) === 'Uint16Array'; +} + +export function isUint32Array(value: unknown): value is Uint32Array { + return getObjectType(value) === 'Uint32Array'; +} + +export function isUint8Array(value: unknown): value is Uint8Array { + return getObjectType(value) === 'Uint8Array'; +} + +export function isUint8ClampedArray(value: unknown): value is Uint8ClampedArray { + return getObjectType(value) === 'Uint8ClampedArray'; +} + +export function isUndefined(value: unknown): value is undefined { + return value === undefined; +} + +export function isUrlInstance(value: unknown): value is URL { + return getObjectType(value) === 'URL'; +} + +// eslint-disable-next-line unicorn/prevent-abbreviations +export function isUrlSearchParams(value: unknown): value is URLSearchParams { + return getObjectType(value) === 'URLSearchParams'; +} + +export function isUrlString(value: unknown): value is string { + if (!isString(value)) { + return false; + } + + try { + new URL(value); // eslint-disable-line no-new + return true; + } catch { + return false; + } +} + +export function isValidLength(value: unknown): value is number { + return isSafeInteger(value) && value >= 0; +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function isWeakMap(value: unknown): value is WeakMap { + return getObjectType(value) === 'WeakMap'; +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function isWeakRef(value: unknown): value is WeakRef { + return getObjectType(value) === 'WeakRef'; +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function isWeakSet(value: unknown): value is WeakSet { + return getObjectType(value) === 'WeakSet'; +} + +export function isWhitespaceString(value: unknown): value is string { + return isString(value) && /^\s+$/.test(value); +} type ArrayMethod = (fn: (value: unknown, index: number, array: unknown[]) => boolean, thisArg?: unknown) => boolean; -const predicateOnArray = (method: ArrayMethod, predicate: Predicate, values: unknown[]) => { - if (!is.function_(predicate)) { +function predicateOnArray(method: ArrayMethod, predicate: Predicate, values: unknown[]) { + if (!isFunction(predicate)) { throw new TypeError(`Invalid predicate: ${JSON.stringify(predicate)}`); } @@ -504,31 +788,17 @@ const predicateOnArray = (method: ArrayMethod, predicate: Predicate, values: unk } return method.call(values, predicate); -}; +} -is.any = (predicate: Predicate | Predicate[], ...values: unknown[]): boolean => { - const predicates = is.array(predicate) ? predicate : [predicate]; - return predicates.some(singlePredicate => - predicateOnArray(Array.prototype.some, singlePredicate, values), - ); -}; +function typeErrorMessage(description: AssertionTypeDescription, value: unknown): string { + return `Expected value which is \`${description}\`, received value of type \`${is(value)}\`.`; +} -is.all = (predicate: Predicate, ...values: unknown[]): boolean => predicateOnArray(Array.prototype.every, predicate, values); - -const assertType = (condition: boolean, description: AssertionTypeDescription, value: unknown, options: {multipleValues?: boolean} = {}): asserts condition => { - if (!condition) { - const {multipleValues} = options; - const valuesMessage = multipleValues - ? `received values of types ${[ - ...new Set( - (value as any[]).map(singleValue => `\`${is(singleValue)}\``), - ), - ].join(', ')}` - : `received value of type \`${is(value)}\``; - - throw new TypeError(`Expected value which is \`${description}\`, ${valuesMessage}.`); - } -}; +function typeErrorMessageMultipleValue(description: AssertionTypeDescription, values: unknown[]): string { + // eslint-disable-next-line unicorn/prefer-spread + const valueTypes = Array.from(new Set(values.map(singleValue => `\`${is(singleValue)}\``))).join(', '); + return `Expected value which is \`${description}\`, received values of types ${valueTypes}.`; +} // Type assertions have to be declared with an explicit type. type Assert = { @@ -540,8 +810,17 @@ type Assert = { negativeNumber: (value: unknown) => asserts value is number; bigint: (value: unknown) => asserts value is bigint; // eslint-disable-next-line @typescript-eslint/ban-types + function: (value: unknown) => asserts value is Function; + /** @deprecated Renamed to `function`. */ + // eslint-disable-next-line @typescript-eslint/ban-types function_: (value: unknown) => asserts value is Function; - null_: (value: unknown) => asserts value is null; // eslint-disable-line @typescript-eslint/ban-types + // eslint-disable-next-line @typescript-eslint/ban-types + null: (value: unknown) => asserts value is null; + /** @deprecated Renamed to `null`. */ + // eslint-disable-next-line @typescript-eslint/ban-types + null_: (value: unknown) => asserts value is null; + class: (value: unknown) => asserts value is Class; + /** @deprecated Renamed to `class`. */ class_: (value: unknown) => asserts value is Class; boolean: (value: unknown) => asserts value is boolean; symbol: (value: unknown) => asserts value is symbol; @@ -549,7 +828,8 @@ type Assert = { array: (value: unknown, assertion?: (element: unknown) => asserts element is T) => asserts value is T[]; buffer: (value: unknown) => asserts value is Buffer; blob: (value: unknown) => asserts value is Blob; - nullOrUndefined: (value: unknown) => asserts value is null | undefined; // eslint-disable-line @typescript-eslint/ban-types + // eslint-disable-next-line @typescript-eslint/ban-types + nullOrUndefined: (value: unknown) => asserts value is null | undefined; object: (value: unknown) => asserts value is Record; iterable: (value: unknown) => asserts value is Iterable; asyncIterable: (value: unknown) => asserts value is AsyncIterable; @@ -568,9 +848,12 @@ type Assert = { error: (value: unknown) => asserts value is Error; map: (value: unknown) => asserts value is Map; set: (value: unknown) => asserts value is Set; - weakMap: (value: unknown) => asserts value is WeakMap; // eslint-disable-line @typescript-eslint/ban-types - weakSet: (value: unknown) => asserts value is WeakSet; // eslint-disable-line @typescript-eslint/ban-types - weakRef: (value: unknown) => asserts value is WeakRef; // eslint-disable-line @typescript-eslint/ban-types + // eslint-disable-next-line @typescript-eslint/ban-types + weakMap: (value: unknown) => asserts value is WeakMap; + // eslint-disable-next-line @typescript-eslint/ban-types + weakSet: (value: unknown) => asserts value is WeakSet; + // eslint-disable-next-line @typescript-eslint/ban-types + weakRef: (value: unknown) => asserts value is WeakRef; int8Array: (value: unknown) => asserts value is Int8Array; uint8Array: (value: unknown) => asserts value is Uint8Array; uint8ClampedArray: (value: unknown) => asserts value is Uint8ClampedArray; @@ -617,6 +900,8 @@ type Assert = { propertyKey: (value: unknown) => asserts value is PropertyKey; formData: (value: unknown) => asserts value is FormData; urlSearchParams: (value: unknown) => asserts value is URLSearchParams; + validLength: (value: unknown) => asserts value is number; + whitespaceString: (value: unknown) => asserts value is string; // Numbers. evenInteger: (value: number) => asserts value is number; @@ -624,148 +909,665 @@ type Assert = { // Two arguments. directInstanceOf: (instance: unknown, class_: Class) => asserts instance is T; - inRange: (value: number, range: number | number[]) => asserts value is number; + inRange: (value: number, range: number | [number, number]) => asserts value is number; // Variadic functions. any: (predicate: Predicate | Predicate[], ...values: unknown[]) => void | never; all: (predicate: Predicate, ...values: unknown[]) => void | never; }; -/* eslint-disable @typescript-eslint/no-confusing-void-expression */ export const assert: Assert = { - // Unknowns. - undefined: (value: unknown): asserts value is undefined => assertType(is.undefined(value), 'undefined', value), - string: (value: unknown): asserts value is string => assertType(is.string(value), 'string', value), - number: (value: unknown): asserts value is number => assertType(is.number(value), 'number', value), - positiveNumber: (value: unknown): asserts value is number => assertType(is.positiveNumber(value), 'positive number', value), - negativeNumber: (value: unknown): asserts value is number => assertType(is.negativeNumber(value), 'negative number', value), - bigint: (value: unknown): asserts value is bigint => assertType(is.bigint(value), 'bigint', value), - // eslint-disable-next-line @typescript-eslint/ban-types - function_: (value: unknown): asserts value is Function => assertType(is.function_(value), 'Function', value), - null_: (value: unknown): asserts value is null => assertType(is.null_(value), 'null', value), // eslint-disable-line @typescript-eslint/ban-types - class_: (value: unknown): asserts value is Class => assertType(is.class_(value), 'Class', value), - boolean: (value: unknown): asserts value is boolean => assertType(is.boolean(value), 'boolean', value), - symbol: (value: unknown): asserts value is symbol => assertType(is.symbol(value), 'symbol', value), - numericString: (value: unknown): asserts value is `${number}` => assertType(is.numericString(value), 'string with a number', value), - array: (value: unknown, assertion?: (element: unknown) => asserts element is T): asserts value is T[] => { // eslint-disable-line object-shorthand - const assert: (condition: boolean, description: AssertionTypeDescription, value: unknown) => asserts condition = assertType; - assert(is.array(value), 'Array', value); - - if (assertion) { - // eslint-disable-next-line unicorn/no-array-for-each, unicorn/no-array-callback-reference - value.forEach(assertion); - } - }, - buffer: (value: unknown): asserts value is Buffer => assertType(is.buffer(value), 'Buffer', value), - blob: (value: unknown): asserts value is Blob => assertType(is.blob(value), 'Blob', value), - nullOrUndefined: (value: unknown): asserts value is null | undefined => assertType(is.nullOrUndefined(value), 'null or undefined', value), // eslint-disable-line @typescript-eslint/ban-types - object: (value: unknown): asserts value is object => assertType(is.object(value), 'Object', value), // eslint-disable-line @typescript-eslint/ban-types - iterable: (value: unknown): asserts value is Iterable => assertType(is.iterable(value), 'Iterable', value), - asyncIterable: (value: unknown): asserts value is AsyncIterable => assertType(is.asyncIterable(value), 'AsyncIterable', value), - generator: (value: unknown): asserts value is Generator => assertType(is.generator(value), 'Generator', value), - asyncGenerator: (value: unknown): asserts value is AsyncGenerator => assertType(is.asyncGenerator(value), 'AsyncGenerator', value), - nativePromise: (value: unknown): asserts value is Promise => assertType(is.nativePromise(value), 'native Promise', value), - promise: (value: unknown): asserts value is Promise => assertType(is.promise(value), 'Promise', value), - generatorFunction: (value: unknown): asserts value is GeneratorFunction => assertType(is.generatorFunction(value), 'GeneratorFunction', value), - asyncGeneratorFunction: (value: unknown): asserts value is AsyncGeneratorFunction => assertType(is.asyncGeneratorFunction(value), 'AsyncGeneratorFunction', value), - // eslint-disable-next-line @typescript-eslint/ban-types - asyncFunction: (value: unknown): asserts value is Function => assertType(is.asyncFunction(value), 'AsyncFunction', value), - // eslint-disable-next-line @typescript-eslint/ban-types - boundFunction: (value: unknown): asserts value is Function => assertType(is.boundFunction(value), 'Function', value), - regExp: (value: unknown): asserts value is RegExp => assertType(is.regExp(value), 'RegExp', value), - date: (value: unknown): asserts value is Date => assertType(is.date(value), 'Date', value), - error: (value: unknown): asserts value is Error => assertType(is.error(value), 'Error', value), - map: (value: unknown): asserts value is Map => assertType(is.map(value), 'Map', value), // eslint-disable-line unicorn/no-array-callback-reference - set: (value: unknown): asserts value is Set => assertType(is.set(value), 'Set', value), - weakMap: (value: unknown): asserts value is WeakMap => assertType(is.weakMap(value), 'WeakMap', value), // eslint-disable-line @typescript-eslint/ban-types - weakSet: (value: unknown): asserts value is WeakSet => assertType(is.weakSet(value), 'WeakSet', value), // eslint-disable-line @typescript-eslint/ban-types - weakRef: (value: unknown): asserts value is WeakRef => assertType(is.weakRef(value), 'WeakRef', value), // eslint-disable-line @typescript-eslint/ban-types - int8Array: (value: unknown): asserts value is Int8Array => assertType(is.int8Array(value), 'Int8Array', value), - uint8Array: (value: unknown): asserts value is Uint8Array => assertType(is.uint8Array(value), 'Uint8Array', value), - uint8ClampedArray: (value: unknown): asserts value is Uint8ClampedArray => assertType(is.uint8ClampedArray(value), 'Uint8ClampedArray', value), - int16Array: (value: unknown): asserts value is Int16Array => assertType(is.int16Array(value), 'Int16Array', value), - uint16Array: (value: unknown): asserts value is Uint16Array => assertType(is.uint16Array(value), 'Uint16Array', value), - int32Array: (value: unknown): asserts value is Int32Array => assertType(is.int32Array(value), 'Int32Array', value), - uint32Array: (value: unknown): asserts value is Uint32Array => assertType(is.uint32Array(value), 'Uint32Array', value), - float32Array: (value: unknown): asserts value is Float32Array => assertType(is.float32Array(value), 'Float32Array', value), - float64Array: (value: unknown): asserts value is Float64Array => assertType(is.float64Array(value), 'Float64Array', value), - bigInt64Array: (value: unknown): asserts value is BigInt64Array => assertType(is.bigInt64Array(value), 'BigInt64Array', value), - bigUint64Array: (value: unknown): asserts value is BigUint64Array => assertType(is.bigUint64Array(value), 'BigUint64Array', value), - arrayBuffer: (value: unknown): asserts value is ArrayBuffer => assertType(is.arrayBuffer(value), 'ArrayBuffer', value), - sharedArrayBuffer: (value: unknown): asserts value is SharedArrayBuffer => assertType(is.sharedArrayBuffer(value), 'SharedArrayBuffer', value), - dataView: (value: unknown): asserts value is DataView => assertType(is.dataView(value), 'DataView', value), - enumCase: (value: unknown, targetEnum: T): asserts value is T[keyof T] => assertType(is.enumCase(value, targetEnum), 'EnumCase', value), - urlInstance: (value: unknown): asserts value is URL => assertType(is.urlInstance(value), 'URL', value), - urlString: (value: unknown): asserts value is string => assertType(is.urlString(value), 'string with a URL', value), - truthy: (value: T | Falsy): asserts value is T => assertType(is.truthy(value), 'truthy', value), - falsy: (value: unknown): asserts value is Falsy => assertType(is.falsy(value), 'falsy', value), - nan: (value: unknown): asserts value is number => assertType(is.nan(value), 'NaN', value), - primitive: (value: unknown): asserts value is Primitive => assertType(is.primitive(value), 'primitive', value), - integer: (value: unknown): asserts value is number => assertType(is.integer(value), 'integer', value), - safeInteger: (value: unknown): asserts value is number => assertType(is.safeInteger(value), 'integer', value), - plainObject: (value: unknown): asserts value is Record => assertType(is.plainObject(value), 'plain object', value), - typedArray: (value: unknown): asserts value is TypedArray => assertType(is.typedArray(value), 'TypedArray', value), - arrayLike: (value: unknown): asserts value is ArrayLike => assertType(is.arrayLike(value), 'array-like', value), - tupleLike: >>(value: unknown, guards: [...T]): asserts value is ResolveTypesOfTypeGuardsTuple => assertType(is.tupleLike(value, guards), 'tuple-like', value), - domElement: (value: unknown): asserts value is HTMLElement => assertType(is.domElement(value), 'HTMLElement', value), - observable: (value: unknown): asserts value is ObservableLike => assertType(is.observable(value), 'Observable', value), - nodeStream: (value: unknown): asserts value is NodeStream => assertType(is.nodeStream(value), 'Node.js Stream', value), - infinite: (value: unknown): asserts value is number => assertType(is.infinite(value), 'infinite number', value), - emptyArray: (value: unknown): asserts value is never[] => assertType(is.emptyArray(value), 'empty array', value), - nonEmptyArray: (value: T | Item[]): asserts value is [Item, ...Item[]] => assertType(is.nonEmptyArray(value), 'non-empty array', value), - emptyString: (value: unknown): asserts value is '' => assertType(is.emptyString(value), 'empty string', value), - emptyStringOrWhitespace: (value: unknown): asserts value is string => assertType(is.emptyStringOrWhitespace(value), 'empty string or whitespace', value), - nonEmptyString: (value: unknown): asserts value is string => assertType(is.nonEmptyString(value), 'non-empty string', value), - nonEmptyStringAndNotWhitespace: (value: unknown): asserts value is string => assertType(is.nonEmptyStringAndNotWhitespace(value), 'non-empty string and not whitespace', value), - emptyObject: (value: unknown): asserts value is Record => assertType(is.emptyObject(value), 'empty object', value), - nonEmptyObject: (value: unknown): asserts value is Record => assertType(is.nonEmptyObject(value), 'non-empty object', value), - emptySet: (value: unknown): asserts value is Set => assertType(is.emptySet(value), 'empty set', value), - nonEmptySet: (value: unknown): asserts value is Set => assertType(is.nonEmptySet(value), 'non-empty set', value), - emptyMap: (value: unknown): asserts value is Map => assertType(is.emptyMap(value), 'empty map', value), - nonEmptyMap: (value: unknown): asserts value is Map => assertType(is.nonEmptyMap(value), 'non-empty map', value), - propertyKey: (value: unknown): asserts value is number => assertType(is.propertyKey(value), 'PropertyKey', value), - formData: (value: unknown): asserts value is FormData => assertType(is.formData(value), 'FormData', value), - urlSearchParams: (value: unknown): asserts value is URLSearchParams => assertType(is.urlSearchParams(value), 'URLSearchParams', value), - - // Numbers. - evenInteger: (value: number): asserts value is number => assertType(is.evenInteger(value), 'even integer', value), - oddInteger: (value: number): asserts value is number => assertType(is.oddInteger(value), 'odd integer', value), - - // Two arguments. - directInstanceOf: (instance: unknown, class_: Class): asserts instance is T => assertType(is.directInstanceOf(instance, class_), 'T', instance), - inRange: (value: number, range: number | number[]): asserts value is number => assertType(is.inRange(value, range), 'in range', value), - - // Variadic functions. - any: (predicate: Predicate | Predicate[], ...values: unknown[]): void | never => assertType(is.any(predicate, ...values), 'predicate returns truthy for any value', values, {multipleValues: true}), - all: (predicate: Predicate, ...values: unknown[]): void | never => assertType(is.all(predicate, ...values), 'predicate returns truthy for all values', values, {multipleValues: true}), + all: assertAll, + any: assertAny, + array: assertArray, + arrayBuffer: assertArrayBuffer, + arrayLike: assertArrayLike, + asyncFunction: assertAsyncFunction, + asyncGenerator: assertAsyncGenerator, + asyncGeneratorFunction: assertAsyncGeneratorFunction, + asyncIterable: assertAsyncIterable, + bigint: assertBigint, + bigInt64Array: assertBigInt64Array, + bigUint64Array: assertBigUint64Array, + blob: assertBlob, + boolean: assertBoolean, + boundFunction: assertBoundFunction, + buffer: assertBuffer, + class: assertClass, + class_: assertClass, + dataView: assertDataView, + date: assertDate, + directInstanceOf: assertDirectInstanceOf, + domElement: assertDomElement, + emptyArray: assertEmptyArray, + emptyMap: assertEmptyMap, + emptyObject: assertEmptyObject, + emptySet: assertEmptySet, + emptyString: assertEmptyString, + emptyStringOrWhitespace: assertEmptyStringOrWhitespace, + enumCase: assertEnumCase, + error: assertError, + evenInteger: assertEvenInteger, + falsy: assertFalsy, + float32Array: assertFloat32Array, + float64Array: assertFloat64Array, + formData: assertFormData, + function: assertFunction, + function_: assertFunction, + generator: assertGenerator, + generatorFunction: assertGeneratorFunction, + infinite: assertInfinite, + inRange: assertInRange, + int16Array: assertInt16Array, + int32Array: assertInt32Array, + int8Array: assertInt8Array, + integer: assertInteger, + iterable: assertIterable, + map: assertMap, + nan: assertNan, + nativePromise: assertNativePromise, + negativeNumber: assertNegativeNumber, + nodeStream: assertNodeStream, + nonEmptyArray: assertNonEmptyArray, + nonEmptyMap: assertNonEmptyMap, + nonEmptyObject: assertNonEmptyObject, + nonEmptySet: assertNonEmptySet, + nonEmptyString: assertNonEmptyString, + nonEmptyStringAndNotWhitespace: assertNonEmptyStringAndNotWhitespace, + null: assertNull, + null_: assertNull, + nullOrUndefined: assertNullOrUndefined, + number: assertNumber, + numericString: assertNumericString, + object: assertObject, + observable: assertObservable, + oddInteger: assertOddInteger, + plainObject: assertPlainObject, + positiveNumber: assertPositiveNumber, + primitive: assertPrimitive, + promise: assertPromise, + propertyKey: assertPropertyKey, + regExp: assertRegExp, + safeInteger: assertSafeInteger, + set: assertSet, + sharedArrayBuffer: assertSharedArrayBuffer, + string: assertString, + symbol: assertSymbol, + truthy: assertTruthy, + tupleLike: assertTupleLike, + typedArray: assertTypedArray, + uint16Array: assertUint16Array, + uint32Array: assertUint32Array, + uint8Array: assertUint8Array, + uint8ClampedArray: assertUint8ClampedArray, + undefined: assertUndefined, + urlInstance: assertUrlInstance, + urlSearchParams: assertUrlSearchParams, + urlString: assertUrlString, + validLength: assertValidLength, + weakMap: assertWeakMap, + weakRef: assertWeakRef, + weakSet: assertWeakSet, + whitespaceString: assertWhitespaceString, }; -/* eslint-enable @typescript-eslint/no-confusing-void-expression */ -// Some few keywords are reserved, but we'll populate them for Node.js users -// See https://github.com/Microsoft/TypeScript/issues/2536 -Object.defineProperties(is, { - class: { - value: is.class_, - }, - function: { - value: is.function_, - }, - null: { - value: is.null_, - }, -}); -Object.defineProperties(assert, { - class: { - value: assert.class_, - }, - function: { - value: assert.function_, - }, - null: { - value: assert.null_, - }, -}); +export function assertAll(predicate: Predicate, ...values: unknown[]): void | never { + if (!isAll(predicate, ...values)) { + throw new TypeError(typeErrorMessageMultipleValue('predicate returns truthy for all values', values)); + } +} + +export function assertAny(predicate: Predicate | Predicate[], ...values: unknown[]): void | never { + if (!isAny(predicate, ...values)) { + throw new TypeError(typeErrorMessageMultipleValue('predicate returns truthy for any value', values)); + } +} + +export function assertArray(value: unknown, assertion?: (element: unknown) => asserts element is T): asserts value is T[] { + if (!isArray(value)) { + throw new TypeError(typeErrorMessage('Array', value)); + } + + if (assertion) { + // eslint-disable-next-line unicorn/no-array-for-each, unicorn/no-array-callback-reference + value.forEach(assertion); + } +} + +export function assertArrayBuffer(value: unknown): asserts value is ArrayBuffer { + if (!isArrayBuffer(value)) { + throw new TypeError(typeErrorMessage('ArrayBuffer', value)); + } +} + +export function assertArrayLike(value: unknown): asserts value is ArrayLike { + if (!isArrayLike(value)) { + throw new TypeError(typeErrorMessage('array-like', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertAsyncFunction(value: unknown): asserts value is Function { + if (!isAsyncFunction(value)) { + throw new TypeError(typeErrorMessage('AsyncFunction', value)); + } +} + +export function assertAsyncGenerator(value: unknown): asserts value is AsyncGenerator { + if (!isAsyncGenerator(value)) { + throw new TypeError(typeErrorMessage('AsyncGenerator', value)); + } +} + +export function assertAsyncGeneratorFunction(value: unknown): asserts value is AsyncGeneratorFunction { + if (!isAsyncGeneratorFunction(value)) { + throw new TypeError(typeErrorMessage('AsyncGeneratorFunction', value)); + } +} + +export function assertAsyncIterable(value: unknown): asserts value is AsyncIterable { + if (!isAsyncIterable(value)) { + throw new TypeError(typeErrorMessage('AsyncIterable', value)); + } +} + +export function assertBigint(value: unknown): asserts value is bigint { + if (!isBigint(value)) { + throw new TypeError(typeErrorMessage('bigint', value)); + } +} + +export function assertBigInt64Array(value: unknown): asserts value is BigInt64Array { + if (!isBigInt64Array(value)) { + throw new TypeError(typeErrorMessage('BigInt64Array', value)); + } +} + +export function assertBigUint64Array(value: unknown): asserts value is BigUint64Array { + if (!isBigUint64Array(value)) { + throw new TypeError(typeErrorMessage('BigUint64Array', value)); + } +} + +export function assertBlob(value: unknown): asserts value is Blob { + if (!isBlob(value)) { + throw new TypeError(typeErrorMessage('Blob', value)); + } +} + +export function assertBoolean(value: unknown): asserts value is boolean { + if (!isBoolean(value)) { + throw new TypeError(typeErrorMessage('boolean', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertBoundFunction(value: unknown): asserts value is Function { + if (!isBoundFunction(value)) { + throw new TypeError(typeErrorMessage('Function', value)); + } +} + +export function assertBuffer(value: unknown): asserts value is Buffer { + if (!isBuffer(value)) { + throw new TypeError(typeErrorMessage('Buffer', value)); + } +} + +export function assertClass(value: unknown): asserts value is Class { + if (!isClass(value)) { + throw new TypeError(typeErrorMessage('Class', value)); + } +} + +export function assertDataView(value: unknown): asserts value is DataView { + if (!isDataView(value)) { + throw new TypeError(typeErrorMessage('DataView', value)); + } +} + +export function assertDate(value: unknown): asserts value is Date { + if (!isDate(value)) { + throw new TypeError(typeErrorMessage('Date', value)); + } +} + +export function assertDirectInstanceOf(instance: unknown, class_: Class): asserts instance is T { + if (!isDirectInstanceOf(instance, class_)) { + throw new TypeError(typeErrorMessage('T', instance)); + } +} + +export function assertEmptyArray(value: unknown): asserts value is never[] { + if (!isEmptyArray(value)) { + throw new TypeError(typeErrorMessage('empty array', value)); + } +} + +export function assertEmptyMap(value: unknown): asserts value is Map { + if (!isEmptyMap(value)) { + throw new TypeError(typeErrorMessage('empty map', value)); + } +} + +export function assertEmptyObject(value: unknown): asserts value is Record { + if (!isEmptyObject(value)) { + throw new TypeError(typeErrorMessage('empty object', value)); + } +} + +export function assertEmptySet(value: unknown): asserts value is Set { + if (!isEmptySet(value)) { + throw new TypeError(typeErrorMessage('empty set', value)); + } +} + +export function assertEmptyString(value: unknown): asserts value is '' { + if (!isEmptyString(value)) { + throw new TypeError(typeErrorMessage('empty string', value)); + } +} + +export function assertEmptyStringOrWhitespace(value: unknown): asserts value is string { + if (!isEmptyStringOrWhitespace(value)) { + throw new TypeError(typeErrorMessage('empty string or whitespace', value)); + } +} + +export function assertEnumCase(value: unknown, targetEnum: T): asserts value is T[keyof T] { + if (!isEnumCase(value, targetEnum)) { + throw new TypeError(typeErrorMessage('EnumCase', value)); + } +} + +export function assertError(value: unknown): asserts value is Error { + if (!isError(value)) { + throw new TypeError(typeErrorMessage('Error', value)); + } +} + +export function assertEvenInteger(value: number): asserts value is number { + if (!isEvenInteger(value)) { + throw new TypeError(typeErrorMessage('even integer', value)); + } +} + +export function assertFalsy(value: unknown): asserts value is Falsy { + if (!isFalsy(value)) { + throw new TypeError(typeErrorMessage('falsy', value)); + } +} + +export function assertFloat32Array(value: unknown): asserts value is Float32Array { + if (!isFloat32Array(value)) { + throw new TypeError(typeErrorMessage('Float32Array', value)); + } +} + +export function assertFloat64Array(value: unknown): asserts value is Float64Array { + if (!isFloat64Array(value)) { + throw new TypeError(typeErrorMessage('Float64Array', value)); + } +} + +export function assertFormData(value: unknown): asserts value is FormData { + if (!isFormData(value)) { + throw new TypeError(typeErrorMessage('FormData', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertFunction(value: unknown): asserts value is Function { + if (!isFunction(value)) { + throw new TypeError(typeErrorMessage('Function', value)); + } +} + +export function assertGenerator(value: unknown): asserts value is Generator { + if (!isGenerator(value)) { + throw new TypeError(typeErrorMessage('Generator', value)); + } +} + +export function assertGeneratorFunction(value: unknown): asserts value is GeneratorFunction { + if (!isGeneratorFunction(value)) { + throw new TypeError(typeErrorMessage('GeneratorFunction', value)); + } +} + +export function assertDomElement(value: unknown): asserts value is HTMLElement { + if (!isDomElement(value)) { + throw new TypeError(typeErrorMessage('HTMLElement', value)); + } +} + +export function assertInfinite(value: unknown): asserts value is number { + if (!isInfinite(value)) { + throw new TypeError(typeErrorMessage('infinite number', value)); + } +} + +export function assertInRange(value: number, range: number | [number, number]): asserts value is number { + if (!isInRange(value, range)) { + throw new TypeError(typeErrorMessage('in range', value)); + } +} + +export function assertInt16Array(value: unknown): asserts value is Int16Array { + if (!isInt16Array(value)) { + throw new TypeError(typeErrorMessage('Int16Array', value)); + } +} + +export function assertInt32Array(value: unknown): asserts value is Int32Array { + if (!isInt32Array(value)) { + throw new TypeError(typeErrorMessage('Int32Array', value)); + } +} + +export function assertInt8Array(value: unknown): asserts value is Int8Array { + if (!isInt8Array(value)) { + throw new TypeError(typeErrorMessage('Int8Array', value)); + } +} + +export function assertInteger(value: unknown): asserts value is number { + if (!isInteger(value)) { + throw new TypeError(typeErrorMessage('integer', value)); + } +} + +export function assertIterable(value: unknown): asserts value is Iterable { + if (!isIterable(value)) { + throw new TypeError(typeErrorMessage('Iterable', value)); + } +} + +export function assertNativePromise(value: unknown): asserts value is Promise { + if (!isNativePromise(value)) { + throw new TypeError(typeErrorMessage('native Promise', value)); + } +} + +export function assertMap(value: unknown): asserts value is Map { + if (!isMap(value)) { + throw new TypeError(typeErrorMessage('Map', value)); + } +} + +export function assertNan(value: unknown): asserts value is number { + if (!isNan(value)) { + throw new TypeError(typeErrorMessage('NaN', value)); + } +} + +export function assertNegativeNumber(value: unknown): asserts value is number { + if (!isNegativeNumber(value)) { + throw new TypeError(typeErrorMessage('negative number', value)); + } +} + +export function assertNodeStream(value: unknown): asserts value is NodeStream { + if (!isNodeStream(value)) { + throw new TypeError(typeErrorMessage('Node.js Stream', value)); + } +} + +export function assertNonEmptyArray(value: T | Item[]): asserts value is [Item, ...Item[]] { + if (!isNonEmptyArray(value)) { + throw new TypeError(typeErrorMessage('non-empty array', value)); + } +} + +export function assertNonEmptyMap(value: unknown): asserts value is Map { + if (!isNonEmptyMap(value)) { + throw new TypeError(typeErrorMessage('non-empty map', value)); + } +} + +export function assertNonEmptyObject(value: unknown): asserts value is Record { + if (!isNonEmptyObject(value)) { + throw new TypeError(typeErrorMessage('non-empty object', value)); + } +} + +export function assertNonEmptySet(value: unknown): asserts value is Set { + if (!isNonEmptySet(value)) { + throw new TypeError(typeErrorMessage('non-empty set', value)); + } +} + +export function assertNonEmptyString(value: unknown): asserts value is string { + if (!isNonEmptyString(value)) { + throw new TypeError(typeErrorMessage('non-empty string', value)); + } +} + +export function assertNonEmptyStringAndNotWhitespace(value: unknown): asserts value is string { + if (!isNonEmptyStringAndNotWhitespace(value)) { + throw new TypeError(typeErrorMessage('non-empty string and not whitespace', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertNull(value: unknown): asserts value is null { + if (!isNull(value)) { + throw new TypeError(typeErrorMessage('null', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertNullOrUndefined(value: unknown): asserts value is null | undefined { + if (!isNullOrUndefined(value)) { + throw new TypeError(typeErrorMessage('null or undefined', value)); + } +} + +export function assertNumber(value: unknown): asserts value is number { + if (!isNumber(value)) { + throw new TypeError(typeErrorMessage('number', value)); + } +} + +export function assertNumericString(value: unknown): asserts value is `${number}` { + if (!isNumericString(value)) { + throw new TypeError(typeErrorMessage('string with a number', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertObject(value: unknown): asserts value is object { + if (!isObject(value)) { + throw new TypeError(typeErrorMessage('Object', value)); + } +} + +export function assertObservable(value: unknown): asserts value is ObservableLike { + if (!isObservable(value)) { + throw new TypeError(typeErrorMessage('Observable', value)); + } +} + +export function assertOddInteger(value: number): asserts value is number { + if (!isOddInteger(value)) { + throw new TypeError(typeErrorMessage('odd integer', value)); + } +} + +export function assertPlainObject(value: unknown): asserts value is Record { + if (!isPlainObject(value)) { + throw new TypeError(typeErrorMessage('plain object', value)); + } +} + +export function assertPositiveNumber(value: unknown): asserts value is number { + if (!isPositiveNumber(value)) { + throw new TypeError(typeErrorMessage('positive number', value)); + } +} + +export function assertPrimitive(value: unknown): asserts value is Primitive { + if (!isPrimitive(value)) { + throw new TypeError(typeErrorMessage('primitive', value)); + } +} + +export function assertPromise(value: unknown): asserts value is Promise { + if (!isPromise(value)) { + throw new TypeError(typeErrorMessage('Promise', value)); + } +} + +export function assertPropertyKey(value: unknown): asserts value is number { + if (!isPropertyKey(value)) { + throw new TypeError(typeErrorMessage('PropertyKey', value)); + } +} + +export function assertRegExp(value: unknown): asserts value is RegExp { + if (!isRegExp(value)) { + throw new TypeError(typeErrorMessage('RegExp', value)); + } +} + +export function assertSafeInteger(value: unknown): asserts value is number { + if (!isSafeInteger(value)) { + throw new TypeError(typeErrorMessage('integer', value)); + } +} + +export function assertSet(value: unknown): asserts value is Set { + if (!isSet(value)) { + throw new TypeError(typeErrorMessage('Set', value)); + } +} + +export function assertSharedArrayBuffer(value: unknown): asserts value is SharedArrayBuffer { + if (!isSharedArrayBuffer(value)) { + throw new TypeError(typeErrorMessage('SharedArrayBuffer', value)); + } +} + +export function assertString(value: unknown): asserts value is string { + if (!isString(value)) { + throw new TypeError(typeErrorMessage('string', value)); + } +} + +export function assertSymbol(value: unknown): asserts value is symbol { + if (!isSymbol(value)) { + throw new TypeError(typeErrorMessage('symbol', value)); + } +} + +export function assertTruthy(value: T | Falsy): asserts value is T { + if (!isTruthy(value)) { + throw new TypeError(typeErrorMessage('truthy', value)); + } +} + +export function assertTupleLike>>(value: unknown, guards: [...T]): asserts value is ResolveTypesOfTypeGuardsTuple { + if (!isTupleLike(value, guards)) { + throw new TypeError(typeErrorMessage('tuple-like', value)); + } +} + +export function assertTypedArray(value: unknown): asserts value is TypedArray { + if (!isTypedArray(value)) { + throw new TypeError(typeErrorMessage('TypedArray', value)); + } +} + +export function assertUint16Array(value: unknown): asserts value is Uint16Array { + if (!isUint16Array(value)) { + throw new TypeError(typeErrorMessage('Uint16Array', value)); + } +} + +export function assertUint32Array(value: unknown): asserts value is Uint32Array { + if (!isUint32Array(value)) { + throw new TypeError(typeErrorMessage('Uint32Array', value)); + } +} + +export function assertUint8Array(value: unknown): asserts value is Uint8Array { + if (!isUint8Array(value)) { + throw new TypeError(typeErrorMessage('Uint8Array', value)); + } +} + +export function assertUint8ClampedArray(value: unknown): asserts value is Uint8ClampedArray { + if (!isUint8ClampedArray(value)) { + throw new TypeError(typeErrorMessage('Uint8ClampedArray', value)); + } +} + +export function assertUndefined(value: unknown): asserts value is undefined { + if (!isUndefined(value)) { + throw new TypeError(typeErrorMessage('undefined', value)); + } +} + +export function assertUrlInstance(value: unknown): asserts value is URL { + if (!isUrlInstance(value)) { + throw new TypeError(typeErrorMessage('URL', value)); + } +} + +// eslint-disable-next-line unicorn/prevent-abbreviations +export function assertUrlSearchParams(value: unknown): asserts value is URLSearchParams { + if (!isUrlSearchParams(value)) { + throw new TypeError(typeErrorMessage('URLSearchParams', value)); + } +} + +export function assertUrlString(value: unknown): asserts value is string { + if (!isUrlString(value)) { + throw new TypeError(typeErrorMessage('string with a URL', value)); + } +} + +export function assertValidLength(value: unknown): asserts value is number { + if (!isValidLength(value)) { + throw new TypeError(typeErrorMessage('valid length', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertWeakMap(value: unknown): asserts value is WeakMap { + if (!isWeakMap(value)) { + throw new TypeError(typeErrorMessage('WeakMap', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertWeakRef(value: unknown): asserts value is WeakRef { + if (!isWeakRef(value)) { + throw new TypeError(typeErrorMessage('WeakRef', value)); + } +} + +// eslint-disable-next-line @typescript-eslint/ban-types +export function assertWeakSet(value: unknown): asserts value is WeakSet { + if (!isWeakSet(value)) { + throw new TypeError(typeErrorMessage('WeakSet', value)); + } +} + +export function assertWhitespaceString(value: unknown): asserts value is string { + if (!isWhitespaceString(value)) { + throw new TypeError(typeErrorMessage('whitespace string', value)); + } +} export default is; -export type {Class, TypedArray, ObservableLike, Primitive} from './types.js'; +export type { + ArrayLike, + Class, + NodeStream, + ObservableLike, + Predicate, + Primitive, + TypedArray, +} from './types.js'; diff --git a/source/types.ts b/source/types.ts index 60411b2..621c771 100644 --- a/source/types.ts +++ b/source/types.ts @@ -62,3 +62,14 @@ export type WeakRef = { // eslint-disable-line @typescript-esl readonly [Symbol.toStringTag]: 'WeakRef'; deref(): T | undefined; }; + +export type ArrayLike = { + readonly [index: number]: T; + readonly length: number; +}; + +export type NodeStream = { + pipe(destination: T, options?: {end?: boolean}): T; +} & NodeJS.EventEmitter; + +export type Predicate = (value: unknown) => boolean; diff --git a/test/test.ts b/test/test.ts index a719c11..1db8d75 100644 --- a/test/test.ts +++ b/test/test.ts @@ -61,7 +61,7 @@ const types = new Map([ typename: 'undefined', }], ['null', { - is: is.null_, + is: is.null, assert: assert.null_, fixtures: [ null, @@ -161,7 +161,7 @@ const types = new Map([ 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>(); } @@ -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.