Compare commits
12 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7821031c66 | ||
|
|
a439305554 | ||
|
|
2d4956e634 | ||
|
|
48df5c429c | ||
|
|
13febb6b01 | ||
|
|
cb4ee0e92c | ||
|
|
54fc09406a | ||
|
|
63be5c0c19 | ||
|
|
ac46b5400d | ||
|
|
47415dc46a | ||
|
|
3b40955b02 | ||
|
|
faf700367e |
12 changed files with 2459 additions and 1168 deletions
7
.github/workflows/main.yml
vendored
7
.github/workflows/main.yml
vendored
|
|
@ -10,10 +10,11 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node-version:
|
node-version:
|
||||||
- 20
|
- 24
|
||||||
|
- 22
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm install
|
- run: npm install
|
||||||
|
|
|
||||||
9
AGENTS.md
Normal file
9
AGENTS.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Notes
|
||||||
|
|
||||||
|
## Branded types for type guards
|
||||||
|
|
||||||
|
TypeScript type guards narrow in both branches. If `is.integer(n)` returns `value is number` and the input is `number`, the false branch computes `Exclude<number, number>` = `never`. This makes common patterns like `if (!is.integer(n)) throw; use(n)` fail because `n` becomes `never` after the guard.
|
||||||
|
|
||||||
|
To avoid this, type guard predicates use branded types (e.g., `number & {readonly __brand: 'Integer'}`, `string & {readonly __brand: 'UrlString'}`). A branded subtype ensures the false branch stays the original type (e.g., `Exclude<number, Integer>` = `number`).
|
||||||
|
|
||||||
|
Assert functions (`asserts value is T`) don't need branded types since they throw on failure and have no false branch. They use plain types like `asserts value is number`.
|
||||||
1
CLAUDE.md
Symbolic link
1
CLAUDE.md
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
AGENTS.md
|
||||||
51
package.json
51
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@sindresorhus/is",
|
"name": "@sindresorhus/is",
|
||||||
"version": "7.2.0",
|
"version": "8.1.0",
|
||||||
"description": "Type check values",
|
"description": "Type check values",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"repository": "sindresorhus/is",
|
"repository": "sindresorhus/is",
|
||||||
|
|
@ -17,11 +17,11 @@
|
||||||
},
|
},
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=22"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "del distribution && tsc",
|
"build": "del distribution && tsc",
|
||||||
"test": "tsc --noEmit && xo && ava",
|
"test": "tsc --noEmit && tsc --project test/tsconfig.json --noEmit && xo && node --experimental-transform-types --test test/test.ts",
|
||||||
"prepare": "npm run build"
|
"prepare": "npm run build"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|
@ -51,31 +51,26 @@
|
||||||
"typeguards",
|
"typeguards",
|
||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"xo": {
|
||||||
"@sindresorhus/tsconfig": "^6.0.0",
|
"rules": {
|
||||||
"@types/jsdom": "^21.1.7",
|
"@typescript-eslint/no-unsafe-enum-comparison": "off",
|
||||||
"@types/node": "^20.14.10",
|
"@typescript-eslint/no-confusing-void-expression": "off",
|
||||||
"@types/zen-observable": "^0.8.7",
|
"@typescript-eslint/no-unsafe-type-assertion": "off",
|
||||||
"ava": "^6.1.3",
|
"@stylistic/operator-linebreak": "off"
|
||||||
"del-cli": "^5.1.0",
|
}
|
||||||
"expect-type": "^0.19.0",
|
|
||||||
"jsdom": "^24.1.0",
|
|
||||||
"rxjs": "^7.8.1",
|
|
||||||
"tempy": "^3.1.0",
|
|
||||||
"tsimp": "2.0.11",
|
|
||||||
"typescript": "5.5.3",
|
|
||||||
"xo": "^0.58.0",
|
|
||||||
"zen-observable": "^0.10.0"
|
|
||||||
},
|
},
|
||||||
"ava": {
|
"devDependencies": {
|
||||||
"environmentVariables": {
|
"@sindresorhus/tsconfig": "^8.1.0",
|
||||||
"TSIMP_DIAG": "error"
|
"@types/jsdom": "^28.0.1",
|
||||||
},
|
"@types/node": "^25.5.2",
|
||||||
"extensions": {
|
"@types/zen-observable": "^0.8.7",
|
||||||
"ts": "module"
|
"del-cli": "^7.0.0",
|
||||||
},
|
"expect-type": "^1.3.0",
|
||||||
"nodeArguments": [
|
"jsdom": "^29.0.2",
|
||||||
"--import=tsimp/import"
|
"rxjs": "^7.8.2",
|
||||||
]
|
"tempy": "^3.2.0",
|
||||||
|
"typescript": "6.0.2",
|
||||||
|
"xo": "^2.0.2",
|
||||||
|
"zen-observable": "^0.10.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
80
readme.md
80
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)
|
||||||
|
|
@ -463,6 +474,10 @@ function foo() {
|
||||||
foo();
|
foo();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
##### .finiteNumber(value)
|
||||||
|
|
||||||
|
Check if `value` is a number and is finite. Excludes `Infinity` and `-Infinity`.
|
||||||
|
|
||||||
##### .positiveNumber(value)
|
##### .positiveNumber(value)
|
||||||
|
|
||||||
Check if `value` is a number and is more than 0.
|
Check if `value` is a number and is more than 0.
|
||||||
|
|
@ -471,6 +486,22 @@ Check if `value` is a number and is more than 0.
|
||||||
|
|
||||||
Check if `value` is a number and is less than 0.
|
Check if `value` is a number and is less than 0.
|
||||||
|
|
||||||
|
##### .nonNegativeNumber(value)
|
||||||
|
|
||||||
|
Check if `value` is a number and is 0 or more.
|
||||||
|
|
||||||
|
##### .positiveInteger(value)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
@ -649,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.
|
||||||
|
|
@ -744,6 +786,44 @@ handleMovieRatingApiResponse({rating: 0.87, title: 'The Matrix'});
|
||||||
handleMovieRatingApiResponse({rating: '🦄'});
|
handleMovieRatingApiResponse({rating: '🦄'});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Negative assertion
|
||||||
|
|
||||||
|
Asserts that `value` is not the specified type. Only exact, type-safe negative assertions are exposed.
|
||||||
|
|
||||||
|
Supported assertions:
|
||||||
|
|
||||||
|
- `assert.not.undefined(value)`
|
||||||
|
- `assert.not.null(value)`
|
||||||
|
- `assert.not.nullOrUndefined(value)`
|
||||||
|
- `assert.not.string(value)`
|
||||||
|
- `assert.not.boolean(value)`
|
||||||
|
- `assert.not.symbol(value)`
|
||||||
|
- `assert.not.bigint(value)`
|
||||||
|
- `assert.not.primitive(value)`
|
||||||
|
|
||||||
|
This intentionally excludes checks that cannot produce a safe TypeScript complement: `number` because `is.number` rejects `NaN`, refinements such as `integer` and `validDate`, and branded structural object checks such as `map` and `date`. Broad object checks such as `object` are also excluded to keep negative assertions limited to primitive and nullish types.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import {assert} from '@sindresorhus/is';
|
||||||
|
|
||||||
|
const value: string | undefined = getValue();
|
||||||
|
|
||||||
|
assert.not.undefined(value);
|
||||||
|
// Throws if `value` is `undefined`. Otherwise, `value` is now typed as `string`.
|
||||||
|
```
|
||||||
|
|
||||||
|
For `unknown` input, exact negative assertions narrow to the remaining representable type:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const value: unknown = getValue();
|
||||||
|
|
||||||
|
assert.not.nullOrUndefined(value);
|
||||||
|
// `value` is now typed as non-nullish.
|
||||||
|
|
||||||
|
assert.not.primitive(value);
|
||||||
|
// `value` is now typed as `object`.
|
||||||
|
```
|
||||||
|
|
||||||
### Optional assertion
|
### Optional assertion
|
||||||
|
|
||||||
Asserts that `value` is `undefined` or satisfies the provided `assertion`.
|
Asserts that `value` is `undefined` or satisfies the provided `assertion`.
|
||||||
|
|
|
||||||
378
source/index.ts
378
source/index.ts
|
|
@ -1,18 +1,33 @@
|
||||||
import type {
|
import type {
|
||||||
ArrayLike,
|
ArrayLike,
|
||||||
Class,
|
Class,
|
||||||
|
EvenInteger,
|
||||||
Falsy,
|
Falsy,
|
||||||
|
FiniteNumber,
|
||||||
|
Integer,
|
||||||
|
NaN as NaNType,
|
||||||
|
NegativeInfinity,
|
||||||
|
NegativeInteger,
|
||||||
|
NegativeNumber,
|
||||||
NodeStream,
|
NodeStream,
|
||||||
NonEmptyString,
|
NonEmptyString,
|
||||||
|
NonNegativeInteger,
|
||||||
|
NonNegativeNumber,
|
||||||
ObservableLike,
|
ObservableLike,
|
||||||
|
OddInteger,
|
||||||
Predicate,
|
Predicate,
|
||||||
Primitive,
|
Primitive,
|
||||||
|
PositiveInfinity,
|
||||||
|
PositiveInteger,
|
||||||
|
PositiveNumber,
|
||||||
|
SafeInteger,
|
||||||
TypedArray,
|
TypedArray,
|
||||||
UrlString,
|
UrlString,
|
||||||
|
ValidLength,
|
||||||
WeakRef,
|
WeakRef,
|
||||||
Whitespace,
|
Whitespace,
|
||||||
} from './types.js';
|
} from './types.ts';
|
||||||
import {keysOf} from './utilities.js';
|
import {keysOf} from './utilities.ts';
|
||||||
|
|
||||||
// From type-fest.
|
// From type-fest.
|
||||||
type ExtractFromGlobalConstructors<Name extends string> =
|
type ExtractFromGlobalConstructors<Name extends string> =
|
||||||
|
|
@ -22,6 +37,15 @@ type ExtractFromGlobalConstructors<Name extends string> =
|
||||||
|
|
||||||
type NodeBuffer = ExtractFromGlobalConstructors<'Buffer'>;
|
type NodeBuffer = ExtractFromGlobalConstructors<'Buffer'>;
|
||||||
|
|
||||||
|
type NumericGuardResult<Input, Branded extends number> =
|
||||||
|
(
|
||||||
|
unknown extends Input
|
||||||
|
? Branded
|
||||||
|
: Input extends number
|
||||||
|
? Branded & Input
|
||||||
|
: number
|
||||||
|
) & Input;
|
||||||
|
|
||||||
const typedArrayTypeNames = [
|
const typedArrayTypeNames = [
|
||||||
'Int8Array',
|
'Int8Array',
|
||||||
'Uint8Array',
|
'Uint8Array',
|
||||||
|
|
@ -99,6 +123,7 @@ function isPrimitiveTypeName(name: unknown): name is PrimitiveTypeName {
|
||||||
export type TypeName = ObjectTypeName | PrimitiveTypeName;
|
export type TypeName = ObjectTypeName | PrimitiveTypeName;
|
||||||
|
|
||||||
const assertionTypeDescriptions = [
|
const assertionTypeDescriptions = [
|
||||||
|
'bound Function',
|
||||||
'positive number',
|
'positive number',
|
||||||
'negative number',
|
'negative number',
|
||||||
'Class',
|
'Class',
|
||||||
|
|
@ -133,7 +158,13 @@ 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',
|
||||||
|
'safe integer',
|
||||||
'T',
|
'T',
|
||||||
'in range',
|
'in range',
|
||||||
'predicate returns truthy for any value',
|
'predicate returns truthy for any value',
|
||||||
|
|
@ -150,7 +181,7 @@ export type AssertionTypeDescription = typeof assertionTypeDescriptions[number];
|
||||||
const getObjectType = (value: unknown): ObjectTypeName | undefined => {
|
const getObjectType = (value: unknown): ObjectTypeName | undefined => {
|
||||||
const objectTypeName = Object.prototype.toString.call(value).slice(8, -1);
|
const objectTypeName = Object.prototype.toString.call(value).slice(8, -1);
|
||||||
|
|
||||||
if (/HTML\w+Element/.test(objectTypeName) && isHtmlElement(value)) {
|
if (/HTML\w+Element/v.test(objectTypeName) && isHtmlElement(value)) {
|
||||||
return 'HTMLElement';
|
return 'HTMLElement';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,6 +197,7 @@ function detect(value: unknown): TypeName {
|
||||||
return 'null';
|
return 'null';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
|
||||||
switch (typeof value) {
|
switch (typeof value) {
|
||||||
case 'undefined': {
|
case 'undefined': {
|
||||||
return 'undefined';
|
return 'undefined';
|
||||||
|
|
@ -211,7 +243,7 @@ function detect(value: unknown): TypeName {
|
||||||
}
|
}
|
||||||
|
|
||||||
const tagType = getObjectType(value);
|
const tagType = getObjectType(value);
|
||||||
if (tagType && tagType !== 'Object') {
|
if (tagType !== undefined && tagType !== 'Object') {
|
||||||
return tagType;
|
return tagType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +251,7 @@ function detect(value: unknown): TypeName {
|
||||||
return 'Promise';
|
return 'Promise';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value instanceof String || value instanceof Boolean || value instanceof Number) {
|
if (isBoxedPrimitiveObject(value)) {
|
||||||
throw new TypeError('Please don\'t use object wrappers for primitive types');
|
throw new TypeError('Please don\'t use object wrappers for primitive types');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,6 +262,23 @@ function hasPromiseApi<T = unknown>(value: unknown): value is Promise<T> {
|
||||||
return isFunction((value as Promise<T>)?.then) && isFunction((value as Promise<T>)?.catch);
|
return isFunction((value as Promise<T>)?.then) && isFunction((value as Promise<T>)?.catch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasBoxedPrimitiveBrand(value: unknown, valueOf: () => unknown): boolean {
|
||||||
|
try {
|
||||||
|
// `Object.prototype.toString` can be spoofed via `Symbol.toStringTag`, but the
|
||||||
|
// boxed primitive `valueOf` methods still enforce the real internal brand.
|
||||||
|
Reflect.apply(valueOf, value, []);
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBoxedPrimitiveObject(value: unknown): boolean {
|
||||||
|
return hasBoxedPrimitiveBrand(value, String.prototype.valueOf)
|
||||||
|
|| hasBoxedPrimitiveBrand(value, Boolean.prototype.valueOf)
|
||||||
|
|| hasBoxedPrimitiveBrand(value, Number.prototype.valueOf);
|
||||||
|
}
|
||||||
|
|
||||||
const is = Object.assign(
|
const is = Object.assign(
|
||||||
detect,
|
detect,
|
||||||
{
|
{
|
||||||
|
|
@ -238,6 +287,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,
|
||||||
|
|
@ -264,6 +314,7 @@ const is = Object.assign(
|
||||||
error: isError,
|
error: isError,
|
||||||
evenInteger: isEvenInteger,
|
evenInteger: isEvenInteger,
|
||||||
falsy: isFalsy,
|
falsy: isFalsy,
|
||||||
|
finiteNumber: isFiniteNumber,
|
||||||
float32Array: isFloat32Array,
|
float32Array: isFloat32Array,
|
||||||
float64Array: isFloat64Array,
|
float64Array: isFloat64Array,
|
||||||
formData: isFormData,
|
formData: isFormData,
|
||||||
|
|
@ -281,6 +332,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,
|
||||||
|
|
@ -289,6 +341,8 @@ const is = Object.assign(
|
||||||
nonEmptySet: isNonEmptySet,
|
nonEmptySet: isNonEmptySet,
|
||||||
nonEmptyString: isNonEmptyString,
|
nonEmptyString: isNonEmptyString,
|
||||||
nonEmptyStringAndNotWhitespace: isNonEmptyStringAndNotWhitespace,
|
nonEmptyStringAndNotWhitespace: isNonEmptyStringAndNotWhitespace,
|
||||||
|
nonNegativeInteger: isNonNegativeInteger,
|
||||||
|
nonNegativeNumber: isNonNegativeNumber,
|
||||||
null: isNull,
|
null: isNull,
|
||||||
nullOrUndefined: isNullOrUndefined,
|
nullOrUndefined: isNullOrUndefined,
|
||||||
number: isNumber,
|
number: isNumber,
|
||||||
|
|
@ -296,7 +350,9 @@ const is = Object.assign(
|
||||||
object: isObject,
|
object: isObject,
|
||||||
observable: isObservable,
|
observable: isObservable,
|
||||||
oddInteger: isOddInteger,
|
oddInteger: isOddInteger,
|
||||||
|
oneOf: isOneOf,
|
||||||
plainObject: isPlainObject,
|
plainObject: isPlainObject,
|
||||||
|
positiveInteger: isPositiveInteger,
|
||||||
positiveNumber: isPositiveNumber,
|
positiveNumber: isPositiveNumber,
|
||||||
primitive: isPrimitive,
|
primitive: isPrimitive,
|
||||||
promise: isPromise,
|
promise: isPromise,
|
||||||
|
|
@ -347,9 +403,13 @@ function validatePredicateArray(predicateArray: readonly Predicate[], allowEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const predicate of predicateArray) {
|
for (const predicate of predicateArray) {
|
||||||
if (!isFunction(predicate)) {
|
validatePredicate(predicate);
|
||||||
throw new TypeError(`Invalid predicate: ${JSON.stringify(predicate)}`);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validatePredicate(predicate: Predicate) {
|
||||||
|
if (!isFunction(predicate)) {
|
||||||
|
throw new TypeError(`Invalid predicate: ${JSON.stringify(predicate)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -430,6 +490,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';
|
||||||
}
|
}
|
||||||
|
|
@ -466,7 +530,7 @@ export function isBoolean(value: unknown): value is boolean {
|
||||||
return value === true || value === false;
|
return value === true || value === false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
export function isBoundFunction(value: unknown): value is Function {
|
export function isBoundFunction(value: unknown): value is Function {
|
||||||
return isFunction(value) && !Object.hasOwn(value, 'prototype');
|
return isFunction(value) && !Object.hasOwn(value, 'prototype');
|
||||||
}
|
}
|
||||||
|
|
@ -475,12 +539,12 @@ export function isBoundFunction(value: unknown): value is Function {
|
||||||
Note: [Prefer using `Uint8Array` instead of `Buffer`.](https://sindresorhus.com/blog/goodbye-nodejs-buffer)
|
Note: [Prefer using `Uint8Array` instead of `Buffer`.](https://sindresorhus.com/blog/goodbye-nodejs-buffer)
|
||||||
*/
|
*/
|
||||||
export function isBuffer(value: unknown): value is NodeBuffer {
|
export function isBuffer(value: unknown): value is NodeBuffer {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
return (value as any)?.constructor?.isBuffer?.(value) ?? false;
|
return (value as any)?.constructor?.isBuffer?.(value) ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isClass<T = unknown>(value: unknown): value is Class<T> {
|
export function isClass<T = unknown>(value: unknown): value is Class<T> {
|
||||||
return isFunction(value) && /^class(\s+|{)/.test(value.toString());
|
return isFunction(value) && /^class(?:\s+|\{)/v.test(value.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isDataView(value: unknown): value is DataView {
|
export function isDataView(value: unknown): value is DataView {
|
||||||
|
|
@ -508,7 +572,7 @@ export function isEmptyMap(value: unknown): value is Map<never, never> {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEmptyObject<Key extends keyof any = string>(value: unknown): value is Record<Key, never> {
|
export function isEmptyObject<Key extends keyof any = string>(value: unknown): value is Record<Key, never> {
|
||||||
return isObject(value) && !isMap(value) && !isSet(value) && Object.keys(value).length === 0;
|
return isObject(value) && !isFunction(value) && !isArray(value) && !isMap(value) && !isSet(value) && Object.keys(value).length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEmptySet(value: unknown): value is Set<never> {
|
export function isEmptySet(value: unknown): value is Set<never> {
|
||||||
|
|
@ -524,16 +588,31 @@ export function isEmptyStringOrWhitespace(value: unknown): value is '' | Whitesp
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEnumCase<T = unknown>(value: unknown, targetEnum: T): value is T[keyof T] {
|
export function isEnumCase<T = unknown>(value: unknown, targetEnum: T): value is T[keyof T] {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
// Numeric enums have reverse mappings (e.g. `Direction[0] = "Up"`), so their runtime object contains both `{ Up: 0 }` and `{ "0": "Up" }`. Filtering out entries that round-trip like a canonical number and point back to an own property leaves only actual enum member values.
|
||||||
return Object.values(targetEnum as any).includes(value as string);
|
const enumObject = targetEnum as Record<PropertyKey, unknown>;
|
||||||
|
|
||||||
|
return Object.entries(enumObject).some(([key, enumValue]) => {
|
||||||
|
if (!isString(enumValue)) {
|
||||||
|
return enumValue === value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const numericKey = Number(key);
|
||||||
|
if (Number.isNaN(numericKey) || String(numericKey) !== key) {
|
||||||
|
return enumValue === value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return enumValue === value && !(Object.hasOwn(enumObject, enumValue) && enumObject[enumValue] === numericKey);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isError(value: unknown): value is Error {
|
export function isError(value: unknown): value is Error {
|
||||||
// TODO: Use `Error.isError` when targeting Node.js 24.`
|
// TODO: Use `Error.isError` when targeting Node.js 24.
|
||||||
return getObjectType(value) === 'Error';
|
return getObjectType(value) === 'Error';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEvenInteger(value: unknown): value is number {
|
// For numeric guards, preserve branded narrowing for `unknown`, keep the false branch usable for plain `number`, and still narrow mixed unions to `number`.
|
||||||
|
export function isEvenInteger<Input>(value: Input): value is NumericGuardResult<Input, EvenInteger>;
|
||||||
|
export function isEvenInteger(value: unknown): boolean {
|
||||||
return isAbsoluteModule2(0)(value);
|
return isAbsoluteModule2(0)(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -542,6 +621,11 @@ export function isFalsy(value: unknown): value is Falsy {
|
||||||
return !value;
|
return !value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isFiniteNumber<Input>(value: Input): value is NumericGuardResult<Input, FiniteNumber>;
|
||||||
|
export function isFiniteNumber(value: unknown): boolean {
|
||||||
|
return Number.isFinite(value);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Support detecting Float16Array when targeting Node.js 24.
|
// TODO: Support detecting Float16Array when targeting Node.js 24.
|
||||||
|
|
||||||
export function isFloat32Array(value: unknown): value is Float32Array {
|
export function isFloat32Array(value: unknown): value is Float32Array {
|
||||||
|
|
@ -556,7 +640,7 @@ export function isFormData(value: unknown): value is FormData {
|
||||||
return getObjectType(value) === 'FormData';
|
return getObjectType(value) === 'FormData';
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
export function isFunction(value: unknown): value is Function {
|
export function isFunction(value: unknown): value is Function {
|
||||||
return typeof value === 'function';
|
return typeof value === 'function';
|
||||||
}
|
}
|
||||||
|
|
@ -569,11 +653,9 @@ export function isGeneratorFunction(value: unknown): value is GeneratorFunction
|
||||||
return getObjectType(value) === 'GeneratorFunction';
|
return getObjectType(value) === 'GeneratorFunction';
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
const NODE_TYPE_ELEMENT = 1; // eslint-disable-line @typescript-eslint/naming-convention
|
||||||
const NODE_TYPE_ELEMENT = 1;
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
const DOM_PROPERTIES_TO_CHECK: Array<(keyof HTMLElement)> = [ // eslint-disable-line @typescript-eslint/naming-convention
|
||||||
const DOM_PROPERTIES_TO_CHECK: Array<(keyof HTMLElement)> = [
|
|
||||||
'innerHTML',
|
'innerHTML',
|
||||||
'ownerDocument',
|
'ownerDocument',
|
||||||
'style',
|
'style',
|
||||||
|
|
@ -589,7 +671,8 @@ export function isHtmlElement(value: unknown): value is HTMLElement {
|
||||||
&& DOM_PROPERTIES_TO_CHECK.every(property => property in value);
|
&& DOM_PROPERTIES_TO_CHECK.every(property => property in value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isInfinite(value: unknown): value is number {
|
export function isInfinite<Input>(value: Input): value is NumericGuardResult<Input, PositiveInfinity | NegativeInfinity>;
|
||||||
|
export function isInfinite(value: unknown): boolean {
|
||||||
return value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY;
|
return value === Number.POSITIVE_INFINITY || value === Number.NEGATIVE_INFINITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -599,6 +682,10 @@ export function isInRange(value: number, range: number | [number, number]): valu
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isArray(range) && range.length === 2) {
|
if (isArray(range) && range.length === 2) {
|
||||||
|
if (Number.isNaN(range[0]) || Number.isNaN(range[1])) {
|
||||||
|
throw new TypeError(`Invalid range: ${JSON.stringify(range)}`);
|
||||||
|
}
|
||||||
|
|
||||||
return value >= Math.min(...range) && value <= Math.max(...range);
|
return value >= Math.min(...range) && value <= Math.max(...range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -617,7 +704,8 @@ export function isInt8Array(value: unknown): value is Int8Array {
|
||||||
return getObjectType(value) === 'Int8Array';
|
return getObjectType(value) === 'Int8Array';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isInteger(value: unknown): value is number {
|
export function isInteger<Input>(value: Input): value is NumericGuardResult<Input, Integer>;
|
||||||
|
export function isInteger(value: unknown): boolean {
|
||||||
return Number.isInteger(value);
|
return Number.isInteger(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -629,7 +717,8 @@ export function isMap<Key = unknown, Value = unknown>(value: unknown): value is
|
||||||
return getObjectType(value) === 'Map';
|
return getObjectType(value) === 'Map';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNan(value: unknown) {
|
export function isNan<Input>(value: Input): value is NumericGuardResult<Input, NaNType>;
|
||||||
|
export function isNan(value: unknown): boolean {
|
||||||
return Number.isNaN(value);
|
return Number.isNaN(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -637,7 +726,13 @@ export function isNativePromise<T = unknown>(value: unknown): value is Promise<T
|
||||||
return getObjectType(value) === 'Promise';
|
return getObjectType(value) === 'Promise';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNegativeNumber(value: unknown): value is number {
|
export function isNegativeInteger<Input>(value: Input): value is NumericGuardResult<Input, NegativeInteger>;
|
||||||
|
export function isNegativeInteger(value: unknown): boolean {
|
||||||
|
return isInteger(value) && value < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNegativeNumber<Input>(value: Input): value is NumericGuardResult<Input, NegativeNumber>;
|
||||||
|
export function isNegativeNumber(value: unknown): boolean {
|
||||||
return isNumber(value) && value < 0;
|
return isNumber(value) && value < 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -656,7 +751,7 @@ export function isNonEmptyMap<Key = unknown, Value = unknown>(value: unknown): v
|
||||||
// TODO: Use `not` operator here to remove `Map` and `Set` from type guard:
|
// TODO: Use `not` operator here to remove `Map` and `Set` from type guard:
|
||||||
// - https://github.com/Microsoft/TypeScript/pull/29317
|
// - https://github.com/Microsoft/TypeScript/pull/29317
|
||||||
export function isNonEmptyObject<Key extends keyof any = string, Value = unknown>(value: unknown): value is Record<Key, Value> {
|
export function isNonEmptyObject<Key extends keyof any = string, Value = unknown>(value: unknown): value is Record<Key, Value> {
|
||||||
return isObject(value) && !isMap(value) && !isSet(value) && Object.keys(value).length > 0;
|
return isObject(value) && !isFunction(value) && !isArray(value) && !isMap(value) && !isSet(value) && Object.keys(value).length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNonEmptySet<T = unknown>(value: unknown): value is Set<T> {
|
export function isNonEmptySet<T = unknown>(value: unknown): value is Set<T> {
|
||||||
|
|
@ -673,12 +768,22 @@ export function isNonEmptyStringAndNotWhitespace(value: unknown): value is NonEm
|
||||||
return isString(value) && !isEmptyStringOrWhitespace(value);
|
return isString(value) && !isEmptyStringOrWhitespace(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
export function isNonNegativeInteger<Input>(value: Input): value is NumericGuardResult<Input, NonNegativeInteger>;
|
||||||
|
export function isNonNegativeInteger(value: unknown): boolean {
|
||||||
|
return isInteger(value) && value >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNonNegativeNumber<Input>(value: Input): value is NumericGuardResult<Input, NonNegativeNumber>;
|
||||||
|
export function isNonNegativeNumber(value: unknown): boolean {
|
||||||
|
return isNumber(value) && value >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function isNull(value: unknown): value is null {
|
export function isNull(value: unknown): value is null {
|
||||||
return value === null;
|
return value === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function isNullOrUndefined(value: unknown): value is null | undefined {
|
export function isNullOrUndefined(value: unknown): value is null | undefined {
|
||||||
return isNull(value) || isUndefined(value);
|
return isNull(value) || isUndefined(value);
|
||||||
}
|
}
|
||||||
|
|
@ -688,10 +793,10 @@ export function isNumber(value: unknown): value is number {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isNumericString(value: unknown): value is `${number}` {
|
export function isNumericString(value: unknown): value is `${number}` {
|
||||||
return isString(value) && !isEmptyStringOrWhitespace(value) && !Number.isNaN(Number(value));
|
return isString(value) && !isEmptyStringOrWhitespace(value) && value === value.trim() && !Number.isNaN(Number(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function isObject(value: unknown): value is object {
|
export function isObject(value: unknown): value is object {
|
||||||
return !isNull(value) && (typeof value === 'object' || isFunction(value));
|
return !isNull(value) && (typeof value === 'object' || isFunction(value));
|
||||||
}
|
}
|
||||||
|
|
@ -701,12 +806,12 @@ export function isObservable(value: unknown): value is ObservableLike {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line no-use-extend-native/no-use-extend-native, @typescript-eslint/no-unsafe-call
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
if (Symbol.observable !== undefined && value === (value as any)[Symbol.observable]?.()) {
|
if (Symbol.observable !== undefined && value === (value as any)[Symbol.observable]?.()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
if (value === (value as any)['@@observable']?.()) {
|
if (value === (value as any)['@@observable']?.()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -714,10 +819,15 @@ export function isObservable(value: unknown): value is ObservableLike {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isOddInteger(value: unknown): value is number {
|
export function isOddInteger<Input>(value: Input): value is NumericGuardResult<Input, OddInteger>;
|
||||||
|
export function isOddInteger(value: unknown): boolean {
|
||||||
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);
|
||||||
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
|
@ -730,7 +840,13 @@ export function isPlainObject<Value = unknown>(value: unknown): value is Record<
|
||||||
return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
|
return (prototype === null || prototype === Object.prototype || Object.getPrototypeOf(prototype) === null) && !(Symbol.toStringTag in value) && !(Symbol.iterator in value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPositiveNumber(value: unknown): value is number {
|
export function isPositiveInteger<Input>(value: Input): value is NumericGuardResult<Input, PositiveInteger>;
|
||||||
|
export function isPositiveInteger(value: unknown): boolean {
|
||||||
|
return isInteger(value) && value > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isPositiveNumber<Input>(value: Input): value is NumericGuardResult<Input, PositiveNumber>;
|
||||||
|
export function isPositiveNumber(value: unknown): boolean {
|
||||||
return isNumber(value) && value > 0;
|
return isNumber(value) && value > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -742,7 +858,7 @@ export function isPromise<T = unknown>(value: unknown): value is Promise<T> {
|
||||||
return isNativePromise(value) || hasPromiseApi(value);
|
return isNativePromise(value) || hasPromiseApi(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// `PropertyKey` is any value that can be used as an object key (string, number, or symbol)
|
// `PropertyKey` is any value that can be used as an object key (string, number, or symbol). Note: NaN is technically `typeof 'number'` and thus fits TypeScript's `PropertyKey`, but we intentionally exclude it here because using NaN as a property key is almost always a mistake.
|
||||||
export function isPropertyKey(value: unknown): value is PropertyKey {
|
export function isPropertyKey(value: unknown): value is PropertyKey {
|
||||||
return isAny([isString, isNumber, isSymbol], value);
|
return isAny([isString, isNumber, isSymbol], value);
|
||||||
}
|
}
|
||||||
|
|
@ -751,7 +867,8 @@ export function isRegExp(value: unknown): value is RegExp {
|
||||||
return getObjectType(value) === 'RegExp';
|
return getObjectType(value) === 'RegExp';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isSafeInteger(value: unknown): value is number {
|
export function isSafeInteger<Input>(value: Input): value is NumericGuardResult<Input, SafeInteger>;
|
||||||
|
export function isSafeInteger(value: unknown): boolean {
|
||||||
return Number.isSafeInteger(value);
|
return Number.isSafeInteger(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -777,8 +894,8 @@ export function isTruthy<T>(value: T | Falsy): value is T {
|
||||||
return Boolean(value);
|
return Boolean(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
type ResolveTypesOfTypeGuardsTuple<TypeGuardsOfT, ResultOfT extends unknown[] = [] > =
|
type ResolveTypesOfTypeGuardsTuple<TypeGuardsOfT, ResultOfT extends unknown[] = []> =
|
||||||
TypeGuardsOfT extends [TypeGuard<infer U>, ...infer TOthers]
|
TypeGuardsOfT extends [TypeGuard<infer U>, ...infer TOthers]
|
||||||
? ResolveTypesOfTypeGuardsTuple<TOthers, [...ResultOfT, U]>
|
? ResolveTypesOfTypeGuardsTuple<TOthers, [...ResultOfT, U]>
|
||||||
: TypeGuardsOfT extends undefined[]
|
: TypeGuardsOfT extends undefined[]
|
||||||
|
|
@ -843,35 +960,34 @@ export function isValidDate(value: unknown): value is Date {
|
||||||
return isDate(value) && !isNan(Number(value));
|
return isDate(value) && !isNan(Number(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isValidLength(value: unknown): value is number {
|
export function isValidLength<Input>(value: Input): value is NumericGuardResult<Input, ValidLength>;
|
||||||
|
export function isValidLength(value: unknown): boolean {
|
||||||
return isSafeInteger(value) && value >= 0;
|
return isSafeInteger(value) && value >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function isWeakMap<Key extends object = object, Value = unknown>(value: unknown): value is WeakMap<Key, Value> {
|
export function isWeakMap<Key extends object = object, Value = unknown>(value: unknown): value is WeakMap<Key, Value> {
|
||||||
return getObjectType(value) === 'WeakMap';
|
return getObjectType(value) === 'WeakMap';
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types, unicorn/prevent-abbreviations
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function isWeakRef(value: unknown): value is WeakRef<object> {
|
export function isWeakRef(value: unknown): value is WeakRef<object> {
|
||||||
return getObjectType(value) === 'WeakRef';
|
return getObjectType(value) === 'WeakRef';
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function isWeakSet(value: unknown): value is WeakSet<object> {
|
export function isWeakSet(value: unknown): value is WeakSet<object> {
|
||||||
return getObjectType(value) === 'WeakSet';
|
return getObjectType(value) === 'WeakSet';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isWhitespaceString(value: unknown): value is Whitespace {
|
export function isWhitespaceString(value: unknown): value is Whitespace {
|
||||||
return isString(value) && /^\s+$/.test(value);
|
return isString(value) && /^\s+$/v.test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArrayMethod = (function_: (value: unknown, index: number, array: unknown[]) => boolean, thisArgument?: unknown) => boolean;
|
type ArrayMethod = (function_: (value: unknown, index: number, array: unknown[]) => boolean, thisArgument?: unknown) => boolean;
|
||||||
|
|
||||||
function predicateOnArray(method: ArrayMethod, predicate: Predicate, values: unknown[]) {
|
function predicateOnArray(method: ArrayMethod, predicate: Predicate, values: unknown[]) {
|
||||||
if (!isFunction(predicate)) {
|
validatePredicate(predicate);
|
||||||
throw new TypeError(`Invalid predicate: ${JSON.stringify(predicate)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (values.length === 0) {
|
if (values.length === 0) {
|
||||||
throw new TypeError('Invalid number of values');
|
throw new TypeError('Invalid number of values');
|
||||||
|
|
@ -884,6 +1000,17 @@ function typeErrorMessage(description: AssertionTypeDescription, value: unknown)
|
||||||
return `Expected value which is \`${description}\`, received value of type \`${is(value)}\`.`;
|
return `Expected value which is \`${description}\`, received value of type \`${is(value)}\`.`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function typeErrorMessageNot(description: AssertionTypeDescription, value: unknown): string {
|
||||||
|
return `Expected value which is not \`${description}\`, received value of type \`${is(value)}\`.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotAssertionResult<Value, Forbidden, UnknownResult> = Exclude<Value, Forbidden> & ([unknown] extends [Value] ? UnknownResult : unknown);
|
||||||
|
|
||||||
|
type NotAssertion<Forbidden, UnknownResult = unknown> = <Value>(value: Value, message?: string) => asserts value is NotAssertionResult<Value, Forbidden, UnknownResult>;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
type UnknownNotPrimitive<Forbidden extends Primitive> = Exclude<Primitive, Forbidden> | object;
|
||||||
|
|
||||||
function unique<T>(values: T[]): T[] {
|
function unique<T>(values: T[]): T[] {
|
||||||
// eslint-disable-next-line unicorn/prefer-spread
|
// eslint-disable-next-line unicorn/prefer-spread
|
||||||
return Array.from(new Set(values));
|
return Array.from(new Set(values));
|
||||||
|
|
@ -899,17 +1026,24 @@ function typeErrorMessageMultipleValues(expectedType: AssertionTypeDescription |
|
||||||
}
|
}
|
||||||
|
|
||||||
// Type assertions have to be declared with an explicit type.
|
// Type assertions have to be declared with an explicit type.
|
||||||
|
// Keep assertion outputs unbranded even when the corresponding `is.*` guard uses a branded subtype.
|
||||||
|
// The brands exist to preserve useful false-branch narrowing for type guards on `number` inputs, which does not apply to `asserts`.
|
||||||
type Assert = {
|
type Assert = {
|
||||||
// Unknowns.
|
// Unknowns.
|
||||||
undefined: (value: unknown, message?: string) => asserts value is undefined;
|
undefined: (value: unknown, message?: string) => asserts value is undefined;
|
||||||
string: (value: unknown, message?: string) => asserts value is string;
|
string: (value: unknown, message?: string) => asserts value is string;
|
||||||
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;
|
||||||
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;
|
||||||
|
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;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
function: (value: unknown, message?: string) => asserts value is Function;
|
function: (value: unknown, message?: string) => asserts value is Function;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
null: (value: unknown, message?: string) => asserts value is null;
|
null: (value: unknown, message?: string) => asserts value is null;
|
||||||
class: <T = unknown>(value: unknown, message?: string) => asserts value is Class<T>;
|
class: <T = unknown>(value: unknown, message?: string) => asserts value is Class<T>;
|
||||||
boolean: (value: unknown, message?: string) => asserts value is boolean;
|
boolean: (value: unknown, message?: string) => asserts value is boolean;
|
||||||
|
|
@ -918,7 +1052,7 @@ type Assert = {
|
||||||
array: <T = unknown>(value: unknown, assertion?: (element: unknown) => asserts element is T, message?: string) => asserts value is T[];
|
array: <T = unknown>(value: unknown, assertion?: (element: unknown) => asserts element is T, message?: string) => asserts value is T[];
|
||||||
buffer: (value: unknown, message?: string) => asserts value is NodeBuffer;
|
buffer: (value: unknown, message?: string) => asserts value is NodeBuffer;
|
||||||
blob: (value: unknown, message?: string) => asserts value is Blob;
|
blob: (value: unknown, message?: string) => asserts value is Blob;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
nullOrUndefined: (value: unknown, message?: string) => asserts value is null | undefined;
|
nullOrUndefined: (value: unknown, message?: string) => asserts value is null | undefined;
|
||||||
object: <Key extends keyof any = string, Value = unknown>(value: unknown, message?: string) => asserts value is Record<Key, Value>;
|
object: <Key extends keyof any = string, Value = unknown>(value: unknown, message?: string) => asserts value is Record<Key, Value>;
|
||||||
iterable: <T = unknown>(value: unknown, message?: string) => asserts value is Iterable<T>;
|
iterable: <T = unknown>(value: unknown, message?: string) => asserts value is Iterable<T>;
|
||||||
|
|
@ -929,20 +1063,20 @@ type Assert = {
|
||||||
promise: <T = unknown>(value: unknown, message?: string) => asserts value is Promise<T>;
|
promise: <T = unknown>(value: unknown, message?: string) => asserts value is Promise<T>;
|
||||||
generatorFunction: (value: unknown, message?: string) => asserts value is GeneratorFunction;
|
generatorFunction: (value: unknown, message?: string) => asserts value is GeneratorFunction;
|
||||||
asyncGeneratorFunction: (value: unknown, message?: string) => asserts value is AsyncGeneratorFunction;
|
asyncGeneratorFunction: (value: unknown, message?: string) => asserts value is AsyncGeneratorFunction;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
asyncFunction: (value: unknown, message?: string) => asserts value is Function;
|
asyncFunction: (value: unknown, message?: string) => asserts value is Function;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
boundFunction: (value: unknown, message?: string) => asserts value is Function;
|
boundFunction: (value: unknown, message?: string) => asserts value is Function;
|
||||||
regExp: (value: unknown, message?: string) => asserts value is RegExp;
|
regExp: (value: unknown, message?: string) => asserts value is RegExp;
|
||||||
date: (value: unknown, message?: string) => asserts value is Date;
|
date: (value: unknown, message?: string) => asserts value is Date;
|
||||||
error: (value: unknown, message?: string) => asserts value is Error;
|
error: (value: unknown, message?: string) => asserts value is Error;
|
||||||
map: <Key = unknown, Value = unknown>(value: unknown, message?: string) => asserts value is Map<Key, Value>;
|
map: <Key = unknown, Value = unknown>(value: unknown, message?: string) => asserts value is Map<Key, Value>;
|
||||||
set: <T = unknown>(value: unknown, message?: string) => asserts value is Set<T>;
|
set: <T = unknown>(value: unknown, message?: string) => asserts value is Set<T>;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
weakMap: <Key extends object = object, Value = unknown>(value: unknown, message?: string) => asserts value is WeakMap<Key, Value>;
|
weakMap: <Key extends object = object, Value = unknown>(value: unknown, message?: string) => asserts value is WeakMap<Key, Value>;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
weakSet: <T extends object = object>(value: unknown, message?: string) => asserts value is WeakSet<T>;
|
weakSet: <T extends object = object>(value: unknown, message?: string) => asserts value is WeakSet<T>;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
weakRef: <T extends object = object>(value: unknown, message?: string) => asserts value is WeakRef<T>;
|
weakRef: <T extends object = object>(value: unknown, message?: string) => asserts value is WeakRef<T>;
|
||||||
int8Array: (value: unknown, message?: string) => asserts value is Int8Array;
|
int8Array: (value: unknown, message?: string) => asserts value is Int8Array;
|
||||||
uint8Array: (value: unknown, message?: string) => asserts value is Uint8Array;
|
uint8Array: (value: unknown, message?: string) => asserts value is Uint8Array;
|
||||||
|
|
@ -1002,6 +1136,8 @@ type Assert = {
|
||||||
directInstanceOf: <T>(instance: unknown, class_: Class<T>, message?: string) => asserts instance is T;
|
directInstanceOf: <T>(instance: unknown, class_: Class<T>, message?: string) => asserts instance is T;
|
||||||
inRange: (value: number, range: number | [number, number], message?: string) => asserts value is number;
|
inRange: (value: number, range: number | [number, number], message?: string) => asserts value is number;
|
||||||
|
|
||||||
|
not: NotAssert;
|
||||||
|
|
||||||
// Variadic functions.
|
// Variadic functions.
|
||||||
any: (predicate: Predicate | readonly Predicate[], ...values: unknown[]) => void | never;
|
any: (predicate: Predicate | readonly Predicate[], ...values: unknown[]) => void | never;
|
||||||
all: (predicate: Predicate | readonly Predicate[], ...values: unknown[]) => void | never;
|
all: (predicate: Predicate | readonly Predicate[], ...values: unknown[]) => void | never;
|
||||||
|
|
@ -1014,9 +1150,58 @@ type Assert = {
|
||||||
optional: <T>(value: unknown, assertion: (value: unknown, message?: string) => asserts value is T, message?: string) => asserts value is T | undefined;
|
optional: <T>(value: unknown, assertion: (value: unknown, message?: string) => asserts value is T, message?: string) => asserts value is T | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type NotAssert = {
|
||||||
|
undefined: NotAssertion<undefined, UnknownNotPrimitive<undefined>>;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
null: NotAssertion<null, UnknownNotPrimitive<null>>;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
nullOrUndefined: NotAssertion<null | undefined, UnknownNotPrimitive<null | undefined>>;
|
||||||
|
string: NotAssertion<string, UnknownNotPrimitive<string>>;
|
||||||
|
boolean: NotAssertion<boolean, UnknownNotPrimitive<boolean>>;
|
||||||
|
symbol: NotAssertion<symbol, UnknownNotPrimitive<symbol>>;
|
||||||
|
bigint: NotAssertion<bigint, UnknownNotPrimitive<bigint>>;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
primitive: NotAssertion<Primitive, object>;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Negative assertions are limited to types where the assertion rejects every TypeScript value assignable to the forbidden type. Structural object types such as `Map`, `Set`, `Date`, and `Array` are excluded because TypeScript accepts shape-compatible mocks while the runtime checks use object brands, so `Exclude` would narrow values that can pass the negative assertion.
|
||||||
|
function createAssertNot<Forbidden, UnknownResult = unknown>(predicate: Predicate, description: AssertionTypeDescription): NotAssertion<Forbidden, UnknownResult> {
|
||||||
|
return <Value>(value: Value, message?: string): asserts value is NotAssertionResult<Value, Forbidden, UnknownResult> => {
|
||||||
|
if (predicate(value)) {
|
||||||
|
throw new TypeError(message ?? typeErrorMessageNot(description, value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const assertNotUndefined: NotAssertion<undefined, UnknownNotPrimitive<undefined>> = createAssertNot<undefined, UnknownNotPrimitive<undefined>>(isUndefined, 'undefined');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
export const assertNotNull: NotAssertion<null, UnknownNotPrimitive<null>> = createAssertNot<null, UnknownNotPrimitive<null>>(isNull, 'null');
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
export const assertNotNullOrUndefined: NotAssertion<null | undefined, UnknownNotPrimitive<null | undefined>>
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
= createAssertNot<null | undefined, UnknownNotPrimitive<null | undefined>>(isNullOrUndefined, 'null or undefined');
|
||||||
|
export const assertNotString: NotAssertion<string, UnknownNotPrimitive<string>> = createAssertNot<string, UnknownNotPrimitive<string>>(isString, 'string');
|
||||||
|
export const assertNotBoolean: NotAssertion<boolean, UnknownNotPrimitive<boolean>> = createAssertNot<boolean, UnknownNotPrimitive<boolean>>(isBoolean, 'boolean');
|
||||||
|
export const assertNotSymbol: NotAssertion<symbol, UnknownNotPrimitive<symbol>> = createAssertNot<symbol, UnknownNotPrimitive<symbol>>(isSymbol, 'symbol');
|
||||||
|
export const assertNotBigint: NotAssertion<bigint, UnknownNotPrimitive<bigint>> = createAssertNot<bigint, UnknownNotPrimitive<bigint>>(isBigint, 'bigint');
|
||||||
|
export const assertNotPrimitive: NotAssertion<Primitive, object> = createAssertNot<Primitive, object>(isPrimitive, 'primitive'); // eslint-disable-line @typescript-eslint/no-restricted-types
|
||||||
|
|
||||||
|
// We intentionally do not support `assert.not(is.undefined, value)`. TypeScript cannot derive safe complement types from arbitrary predicates, and many predicates here are refinements (for example, `is.number` rejects `NaN`). Explicit methods keep runtime checks and type narrowing aligned.
|
||||||
|
const notAssertions: NotAssert = {
|
||||||
|
bigint: assertNotBigint,
|
||||||
|
boolean: assertNotBoolean,
|
||||||
|
null: assertNotNull,
|
||||||
|
nullOrUndefined: assertNotNullOrUndefined,
|
||||||
|
primitive: assertNotPrimitive,
|
||||||
|
string: assertNotString,
|
||||||
|
symbol: assertNotSymbol,
|
||||||
|
undefined: assertNotUndefined,
|
||||||
|
};
|
||||||
|
|
||||||
export const assert: Assert = {
|
export const assert: Assert = {
|
||||||
all: assertAll,
|
all: assertAll,
|
||||||
any: assertAny,
|
any: assertAny,
|
||||||
|
not: notAssertions,
|
||||||
optional: assertOptional,
|
optional: assertOptional,
|
||||||
array: assertArray,
|
array: assertArray,
|
||||||
arrayBuffer: assertArrayBuffer,
|
arrayBuffer: assertArrayBuffer,
|
||||||
|
|
@ -1046,6 +1231,7 @@ export const assert: Assert = {
|
||||||
error: assertError,
|
error: assertError,
|
||||||
evenInteger: assertEvenInteger,
|
evenInteger: assertEvenInteger,
|
||||||
falsy: assertFalsy,
|
falsy: assertFalsy,
|
||||||
|
finiteNumber: assertFiniteNumber,
|
||||||
float32Array: assertFloat32Array,
|
float32Array: assertFloat32Array,
|
||||||
float64Array: assertFloat64Array,
|
float64Array: assertFloat64Array,
|
||||||
formData: assertFormData,
|
formData: assertFormData,
|
||||||
|
|
@ -1063,6 +1249,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,
|
||||||
|
|
@ -1071,6 +1258,8 @@ export const assert: Assert = {
|
||||||
nonEmptySet: assertNonEmptySet,
|
nonEmptySet: assertNonEmptySet,
|
||||||
nonEmptyString: assertNonEmptyString,
|
nonEmptyString: assertNonEmptyString,
|
||||||
nonEmptyStringAndNotWhitespace: assertNonEmptyStringAndNotWhitespace,
|
nonEmptyStringAndNotWhitespace: assertNonEmptyStringAndNotWhitespace,
|
||||||
|
nonNegativeInteger: assertNonNegativeInteger,
|
||||||
|
nonNegativeNumber: assertNonNegativeNumber,
|
||||||
null: assertNull,
|
null: assertNull,
|
||||||
nullOrUndefined: assertNullOrUndefined,
|
nullOrUndefined: assertNullOrUndefined,
|
||||||
number: assertNumber,
|
number: assertNumber,
|
||||||
|
|
@ -1079,6 +1268,7 @@ export const assert: Assert = {
|
||||||
observable: assertObservable,
|
observable: assertObservable,
|
||||||
oddInteger: assertOddInteger,
|
oddInteger: assertOddInteger,
|
||||||
plainObject: assertPlainObject,
|
plainObject: assertPlainObject,
|
||||||
|
positiveInteger: assertPositiveInteger,
|
||||||
positiveNumber: assertPositiveNumber,
|
positiveNumber: assertPositiveNumber,
|
||||||
primitive: assertPrimitive,
|
primitive: assertPrimitive,
|
||||||
promise: assertPromise,
|
promise: assertPromise,
|
||||||
|
|
@ -1121,7 +1311,7 @@ const methodTypeMap = {
|
||||||
isBigUint64Array: 'BigUint64Array',
|
isBigUint64Array: 'BigUint64Array',
|
||||||
isBlob: 'Blob',
|
isBlob: 'Blob',
|
||||||
isBoolean: 'boolean',
|
isBoolean: 'boolean',
|
||||||
isBoundFunction: 'Function',
|
isBoundFunction: 'bound Function',
|
||||||
isBuffer: 'Buffer',
|
isBuffer: 'Buffer',
|
||||||
isClass: 'Class',
|
isClass: 'Class',
|
||||||
isDataView: 'DataView',
|
isDataView: 'DataView',
|
||||||
|
|
@ -1137,6 +1327,7 @@ const methodTypeMap = {
|
||||||
isError: 'Error',
|
isError: 'Error',
|
||||||
isEvenInteger: 'even integer',
|
isEvenInteger: 'even integer',
|
||||||
isFalsy: 'falsy',
|
isFalsy: 'falsy',
|
||||||
|
isFiniteNumber: 'finite number',
|
||||||
isFloat32Array: 'Float32Array',
|
isFloat32Array: 'Float32Array',
|
||||||
isFloat64Array: 'Float64Array',
|
isFloat64Array: 'Float64Array',
|
||||||
isFormData: 'FormData',
|
isFormData: 'FormData',
|
||||||
|
|
@ -1154,6 +1345,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',
|
||||||
|
|
@ -1162,6 +1354,8 @@ 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',
|
||||||
isNull: 'null',
|
isNull: 'null',
|
||||||
isNullOrUndefined: 'null or undefined',
|
isNullOrUndefined: 'null or undefined',
|
||||||
isNumber: 'number',
|
isNumber: 'number',
|
||||||
|
|
@ -1170,12 +1364,13 @@ const methodTypeMap = {
|
||||||
isObservable: 'Observable',
|
isObservable: 'Observable',
|
||||||
isOddInteger: 'odd integer',
|
isOddInteger: 'odd integer',
|
||||||
isPlainObject: 'plain object',
|
isPlainObject: 'plain object',
|
||||||
|
isPositiveInteger: 'positive integer',
|
||||||
isPositiveNumber: 'positive number',
|
isPositiveNumber: 'positive number',
|
||||||
isPrimitive: 'primitive',
|
isPrimitive: 'primitive',
|
||||||
isPromise: 'Promise',
|
isPromise: 'Promise',
|
||||||
isPropertyKey: 'PropertyKey',
|
isPropertyKey: 'PropertyKey',
|
||||||
isRegExp: 'RegExp',
|
isRegExp: 'RegExp',
|
||||||
isSafeInteger: 'integer',
|
isSafeInteger: 'safe integer',
|
||||||
isSet: 'Set',
|
isSet: 'Set',
|
||||||
isSharedArrayBuffer: 'SharedArrayBuffer',
|
isSharedArrayBuffer: 'SharedArrayBuffer',
|
||||||
isString: 'string',
|
isString: 'string',
|
||||||
|
|
@ -1261,7 +1456,7 @@ export function assertArrayLike<T = unknown>(value: unknown, message?: string):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
export function assertAsyncFunction(value: unknown, message?: string): asserts value is Function {
|
export function assertAsyncFunction(value: unknown, message?: string): asserts value is Function {
|
||||||
if (!isAsyncFunction(value)) {
|
if (!isAsyncFunction(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('AsyncFunction', value));
|
throw new TypeError(message ?? typeErrorMessage('AsyncFunction', value));
|
||||||
|
|
@ -1316,10 +1511,10 @@ export function assertBoolean(value: unknown, message?: string): asserts value i
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
export function assertBoundFunction(value: unknown, message?: string): asserts value is Function {
|
export function assertBoundFunction(value: unknown, message?: string): asserts value is Function {
|
||||||
if (!isBoundFunction(value)) {
|
if (!isBoundFunction(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('Function', value));
|
throw new TypeError(message ?? typeErrorMessage('bound Function', value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1416,6 +1611,12 @@ export function assertFalsy(value: unknown, message?: string): asserts value is
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assertFiniteNumber(value: unknown, message?: string): asserts value is number {
|
||||||
|
if (!isFiniteNumber(value)) {
|
||||||
|
throw new TypeError(message ?? typeErrorMessage('finite number', value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function assertFloat32Array(value: unknown, message?: string): asserts value is Float32Array {
|
export function assertFloat32Array(value: unknown, message?: string): asserts value is Float32Array {
|
||||||
if (!isFloat32Array(value)) {
|
if (!isFloat32Array(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('Float32Array', value));
|
throw new TypeError(message ?? typeErrorMessage('Float32Array', value));
|
||||||
|
|
@ -1434,7 +1635,7 @@ export function assertFormData(value: unknown, message?: string): asserts value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
export function assertFunction(value: unknown, message?: string): asserts value is Function {
|
export function assertFunction(value: unknown, message?: string): asserts value is Function {
|
||||||
if (!isFunction(value)) {
|
if (!isFunction(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('Function', value));
|
throw new TypeError(message ?? typeErrorMessage('Function', value));
|
||||||
|
|
@ -1519,6 +1720,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));
|
||||||
|
|
@ -1567,14 +1774,26 @@ export function assertNonEmptyStringAndNotWhitespace(value: unknown, message?: s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
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 {
|
||||||
|
if (!isNonNegativeNumber(value)) {
|
||||||
|
throw new TypeError(message ?? typeErrorMessage('non-negative number', value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function assertNull(value: unknown, message?: string): asserts value is null {
|
export function assertNull(value: unknown, message?: string): asserts value is null {
|
||||||
if (!isNull(value)) {
|
if (!isNull(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('null', value));
|
throw new TypeError(message ?? typeErrorMessage('null', value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function assertNullOrUndefined(value: unknown, message?: string): asserts value is null | undefined {
|
export function assertNullOrUndefined(value: unknown, message?: string): asserts value is null | undefined {
|
||||||
if (!isNullOrUndefined(value)) {
|
if (!isNullOrUndefined(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('null or undefined', value));
|
throw new TypeError(message ?? typeErrorMessage('null or undefined', value));
|
||||||
|
|
@ -1593,7 +1812,7 @@ export function assertNumericString(value: unknown, message?: string): asserts v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function assertObject(value: unknown, message?: string): asserts value is object {
|
export function assertObject(value: unknown, message?: string): asserts value is object {
|
||||||
if (!isObject(value)) {
|
if (!isObject(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('Object', value));
|
throw new TypeError(message ?? typeErrorMessage('Object', value));
|
||||||
|
|
@ -1618,6 +1837,12 @@ export function assertPlainObject<Value = unknown>(value: unknown, message?: str
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function assertPositiveInteger(value: unknown, message?: string): asserts value is number {
|
||||||
|
if (!isPositiveInteger(value)) {
|
||||||
|
throw new TypeError(message ?? typeErrorMessage('positive integer', value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function assertPositiveNumber(value: unknown, message?: string): asserts value is number {
|
export function assertPositiveNumber(value: unknown, message?: string): asserts value is number {
|
||||||
if (!isPositiveNumber(value)) {
|
if (!isPositiveNumber(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('positive number', value));
|
throw new TypeError(message ?? typeErrorMessage('positive number', value));
|
||||||
|
|
@ -1636,7 +1861,7 @@ export function assertPromise<T = unknown>(value: unknown, message?: string): as
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assertPropertyKey(value: unknown, message?: string): asserts value is number {
|
export function assertPropertyKey(value: unknown, message?: string): asserts value is PropertyKey {
|
||||||
if (!isPropertyKey(value)) {
|
if (!isPropertyKey(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('PropertyKey', value));
|
throw new TypeError(message ?? typeErrorMessage('PropertyKey', value));
|
||||||
}
|
}
|
||||||
|
|
@ -1650,7 +1875,7 @@ export function assertRegExp(value: unknown, message?: string): asserts value is
|
||||||
|
|
||||||
export function assertSafeInteger(value: unknown, message?: string): asserts value is number {
|
export function assertSafeInteger(value: unknown, message?: string): asserts value is number {
|
||||||
if (!isSafeInteger(value)) {
|
if (!isSafeInteger(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('integer', value));
|
throw new TypeError(message ?? typeErrorMessage('safe integer', value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1757,21 +1982,21 @@ export function assertValidLength(value: unknown, message?: string): asserts val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function assertWeakMap<Key extends object = object, Value = unknown>(value: unknown, message?: string): asserts value is WeakMap<Key, Value> {
|
export function assertWeakMap<Key extends object = object, Value = unknown>(value: unknown, message?: string): asserts value is WeakMap<Key, Value> {
|
||||||
if (!isWeakMap(value)) {
|
if (!isWeakMap(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('WeakMap', value));
|
throw new TypeError(message ?? typeErrorMessage('WeakMap', value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types, unicorn/prevent-abbreviations
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function assertWeakRef<T extends object = object>(value: unknown, message?: string): asserts value is WeakRef<T> {
|
export function assertWeakRef<T extends object = object>(value: unknown, message?: string): asserts value is WeakRef<T> {
|
||||||
if (!isWeakRef(value)) {
|
if (!isWeakRef(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('WeakRef', value));
|
throw new TypeError(message ?? typeErrorMessage('WeakRef', value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export function assertWeakSet<T extends object = object>(value: unknown, message?: string): asserts value is WeakSet<T> {
|
export function assertWeakSet<T extends object = object>(value: unknown, message?: string): asserts value is WeakSet<T> {
|
||||||
if (!isWeakSet(value)) {
|
if (!isWeakSet(value)) {
|
||||||
throw new TypeError(message ?? typeErrorMessage('WeakSet', value));
|
throw new TypeError(message ?? typeErrorMessage('WeakSet', value));
|
||||||
|
|
@ -1789,10 +2014,25 @@ export default is;
|
||||||
export type {
|
export type {
|
||||||
ArrayLike,
|
ArrayLike,
|
||||||
Class,
|
Class,
|
||||||
|
EvenInteger,
|
||||||
|
FiniteNumber,
|
||||||
|
Integer,
|
||||||
|
NaN,
|
||||||
|
NegativeInfinity,
|
||||||
|
NegativeInteger,
|
||||||
|
NegativeNumber,
|
||||||
NodeStream,
|
NodeStream,
|
||||||
|
NonNegativeInteger,
|
||||||
|
NonNegativeNumber,
|
||||||
ObservableLike,
|
ObservableLike,
|
||||||
|
OddInteger,
|
||||||
|
PositiveInfinity,
|
||||||
|
PositiveInteger,
|
||||||
|
PositiveNumber,
|
||||||
Predicate,
|
Predicate,
|
||||||
Primitive,
|
Primitive,
|
||||||
|
SafeInteger,
|
||||||
TypedArray,
|
TypedArray,
|
||||||
UrlString,
|
UrlString,
|
||||||
} from './types.js';
|
ValidLength,
|
||||||
|
} from './types.ts';
|
||||||
|
|
|
||||||
121
source/types.ts
121
source/types.ts
|
|
@ -4,7 +4,8 @@
|
||||||
Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
|
Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
|
||||||
*/
|
*/
|
||||||
export type Primitive =
|
export type Primitive =
|
||||||
| null // eslint-disable-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
| null
|
||||||
| undefined
|
| undefined
|
||||||
| string
|
| string
|
||||||
| number
|
| number
|
||||||
|
|
@ -53,10 +54,11 @@ export type ObservableLike = {
|
||||||
[Symbol.observable](): ObservableLike;
|
[Symbol.observable](): ObservableLike;
|
||||||
};
|
};
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
export type Falsy = false | 0 | 0n | '' | null | undefined;
|
export type Falsy = false | 0 | 0n | '' | null | undefined;
|
||||||
|
|
||||||
export type WeakRef<T extends object> = { // eslint-disable-line @typescript-eslint/ban-types, unicorn/prevent-abbreviations
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
export type WeakRef<T extends object> = {
|
||||||
readonly [Symbol.toStringTag]: 'WeakRef';
|
readonly [Symbol.toStringTag]: 'WeakRef';
|
||||||
deref(): T | undefined;
|
deref(): T | undefined;
|
||||||
};
|
};
|
||||||
|
|
@ -76,9 +78,122 @@ export type NonEmptyString = string & {0: string};
|
||||||
|
|
||||||
export type Whitespace = ' ';
|
export type Whitespace = ' ';
|
||||||
|
|
||||||
|
type Brand<Key extends string> = Readonly<Record<Key, true>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A string that represents a valid URL.
|
A string that represents a valid URL.
|
||||||
|
|
||||||
This is a branded type to prevent incorrect TypeScript type narrowing.
|
This is a branded type to prevent incorrect TypeScript type narrowing.
|
||||||
*/
|
*/
|
||||||
export type UrlString = string & {readonly __brand: 'UrlString'};
|
export type UrlString = string & {readonly __brand: 'UrlString'};
|
||||||
|
|
||||||
|
// Keep numeric guards branded and simple. This intentionally favors correct false-branch narrowing for `number` inputs over perfect success-branch narrowing for numeric literal unions.
|
||||||
|
|
||||||
|
/**
|
||||||
|
The IEEE 754 "Not-a-Number" value, typed as a subtype of `number`.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type NaN = number & Brand<'__nanBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A finite number (excludes `NaN`, `Infinity`, and `-Infinity`).
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type FiniteNumber = number & Brand<'__finiteNumberBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A number greater than or equal to zero.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type NonNegativeNumber = number & Brand<'__nonNegativeNumberBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
An integer value (no fractional part).
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type Integer = FiniteNumber & Brand<'__integerBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A number greater than zero.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type PositiveNumber = NonNegativeNumber & Brand<'__positiveNumberBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A number less than zero.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type NegativeNumber = number & Brand<'__negativeNumberBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
An integer less than zero.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type NegativeInteger = Integer & NegativeNumber & Brand<'__negativeIntegerBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
An integer greater than or equal to zero.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type NonNegativeInteger = Integer & NonNegativeNumber & Brand<'__nonNegativeIntegerBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
An integer greater than zero.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type PositiveInteger = NonNegativeInteger & PositiveNumber & Brand<'__positiveIntegerBrand'>;
|
||||||
|
|
||||||
|
// Note: type-fest uses the `1e999` overflow trick to represent these types (since TypeScript has
|
||||||
|
// no built-in Infinity type), but we use branded types here for consistency and to avoid
|
||||||
|
// relying on numeric overflow behavior.
|
||||||
|
|
||||||
|
/**
|
||||||
|
A positive infinite number (`Infinity`).
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type PositiveInfinity = PositiveNumber & Brand<'__positiveInfinityBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A negative infinite number (`-Infinity`).
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type NegativeInfinity = NegativeNumber & Brand<'__negativeInfinityBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A safe integer (within the range of `Number.MIN_SAFE_INTEGER` to `Number.MAX_SAFE_INTEGER`).
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type SafeInteger = Integer & Brand<'__safeIntegerBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
An even integer.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type EvenInteger = Integer & Brand<'__evenIntegerBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
An odd integer.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type OddInteger = Integer & Brand<'__oddIntegerBrand'>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A non-negative safe integer, suitable as an array or string length.
|
||||||
|
|
||||||
|
Branded to prevent false-branch narrowing to `never` when the input is `number`.
|
||||||
|
*/
|
||||||
|
export type ValidLength = SafeInteger & NonNegativeInteger & Brand<'__validLengthBrand'>;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
export function keysOf<T extends Record<PropertyKey, unknown>>(value: T): Array<keyof T> {
|
export function keysOf<T extends Record<PropertyKey, unknown>>(value: T): Array<keyof T> {
|
||||||
return Object.keys(value) as Array<keyof T>;
|
return Object.keys(value) as Array<keyof T>; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2555
test/test.ts
2555
test/test.ts
File diff suppressed because it is too large
Load diff
12
test/tsconfig.json
Normal file
12
test/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{
|
||||||
|
"extends": "../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "..",
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"../source",
|
||||||
|
"type-tests.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
405
test/type-tests.ts
Normal file
405
test/type-tests.ts
Normal file
|
|
@ -0,0 +1,405 @@
|
||||||
|
import {expectTypeOf} from 'expect-type';
|
||||||
|
import is, {
|
||||||
|
assert as isAssert,
|
||||||
|
assertNotNullOrUndefined,
|
||||||
|
assertNotPrimitive,
|
||||||
|
assertNotString,
|
||||||
|
assertNotUndefined,
|
||||||
|
type EvenInteger,
|
||||||
|
type FiniteNumber,
|
||||||
|
type Integer,
|
||||||
|
type NaN as NaNType,
|
||||||
|
type NegativeInfinity,
|
||||||
|
type NegativeInteger,
|
||||||
|
type NegativeNumber,
|
||||||
|
type NonNegativeInteger,
|
||||||
|
type NonNegativeNumber,
|
||||||
|
type OddInteger,
|
||||||
|
type PositiveInfinity,
|
||||||
|
type PositiveInteger,
|
||||||
|
type PositiveNumber,
|
||||||
|
type Primitive,
|
||||||
|
type SafeInteger,
|
||||||
|
type ValidLength,
|
||||||
|
} from '../source/index.ts';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
type UnknownNotPrimitive<Forbidden extends Primitive> = Exclude<Primitive, Forbidden> | object;
|
||||||
|
|
||||||
|
// For each predicate, verify two things:
|
||||||
|
// 1. True branch narrows to the branded type.
|
||||||
|
// 2. False branch on a `number` input stays `number` (not `never`).
|
||||||
|
// Without the branded types, `Exclude<number, number>` = `never` would break
|
||||||
|
// the common validation-guard pattern: if (!is.X(n)) throw; use(n).
|
||||||
|
|
||||||
|
const nanCheck = (value: number) => {
|
||||||
|
if (is.nan(value)) {
|
||||||
|
const _: NaNType = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const finiteNumberCheck = (value: number) => {
|
||||||
|
if (is.finiteNumber(value)) {
|
||||||
|
const _: FiniteNumber = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nonNegativeNumberCheck = (value: number) => {
|
||||||
|
if (is.nonNegativeNumber(value)) {
|
||||||
|
const _: NonNegativeNumber = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const positiveIntegerCheck = (value: number) => {
|
||||||
|
if (is.positiveInteger(value)) {
|
||||||
|
const _: PositiveInteger = value;
|
||||||
|
const __: Integer = value;
|
||||||
|
const ___: NonNegativeInteger = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const negativeIntegerCheck = (value: number) => {
|
||||||
|
if (is.negativeInteger(value)) {
|
||||||
|
const _: NegativeInteger = value;
|
||||||
|
const __: Integer = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const nonNegativeIntegerCheck = (value: number) => {
|
||||||
|
if (is.nonNegativeInteger(value)) {
|
||||||
|
const _: NonNegativeInteger = value;
|
||||||
|
const __: Integer = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const infiniteCheck = (value: number) => {
|
||||||
|
if (is.infinite(value)) {
|
||||||
|
const _: PositiveInfinity | NegativeInfinity = value;
|
||||||
|
const __: PositiveNumber | NegativeNumber = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const integerCheck = (value: number) => {
|
||||||
|
if (is.integer(value)) {
|
||||||
|
const _: Integer = value;
|
||||||
|
const __: FiniteNumber = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const safeIntegerCheck = (value: number) => {
|
||||||
|
if (is.safeInteger(value)) {
|
||||||
|
const _: SafeInteger = value;
|
||||||
|
const __: Integer = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const evenIntegerCheck = (value: number) => {
|
||||||
|
if (is.evenInteger(value)) {
|
||||||
|
const _: EvenInteger = value;
|
||||||
|
const __: Integer = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const oddIntegerCheck = (value: number) => {
|
||||||
|
if (is.oddInteger(value)) {
|
||||||
|
const _: OddInteger = value;
|
||||||
|
const __: Integer = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const positiveNumberCheck = (value: number) => {
|
||||||
|
if (is.positiveNumber(value)) {
|
||||||
|
const _: PositiveNumber = value;
|
||||||
|
const __: NonNegativeNumber = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const negativeNumberCheck = (value: number) => {
|
||||||
|
if (is.negativeNumber(value)) {
|
||||||
|
const _: NegativeNumber = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const validLengthCheck = (value: number) => {
|
||||||
|
if (is.validLength(value)) {
|
||||||
|
const _: ValidLength = value;
|
||||||
|
const __: SafeInteger = value;
|
||||||
|
const ___: NonNegativeInteger = value;
|
||||||
|
} else {
|
||||||
|
const _: number = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const integerUnknownCheck = (value: unknown) => {
|
||||||
|
if (is.integer(value)) {
|
||||||
|
const _: Integer = value;
|
||||||
|
const __: FiniteNumber = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const positiveIntegerUnknownCheck = (value: unknown) => {
|
||||||
|
if (is.positiveInteger(value)) {
|
||||||
|
const _: PositiveInteger = value;
|
||||||
|
const __: NonNegativeInteger = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const integerMixedUnionCheck = (value: string | number) => {
|
||||||
|
if (is.integer(value)) {
|
||||||
|
const _: number = value;
|
||||||
|
} else {
|
||||||
|
const _: string = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const positiveNumberMixedUnionCheck = (value: string | number) => {
|
||||||
|
if (is.positiveNumber(value)) {
|
||||||
|
const _: number = value;
|
||||||
|
} else {
|
||||||
|
const _: string = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const chainedNumericGuardCheck = (value: number) => {
|
||||||
|
if (is.positiveNumber(value) && is.integer(value)) {
|
||||||
|
const _: PositiveNumber = value;
|
||||||
|
const __: Integer = value;
|
||||||
|
const ___: FiniteNumber = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const distinctNumericBrandsStayDistinct = (
|
||||||
|
positiveInteger: PositiveInteger,
|
||||||
|
negativeInteger: NegativeInteger,
|
||||||
|
validLength: ValidLength,
|
||||||
|
) => {
|
||||||
|
// @ts-expect-error -- Distinct numeric refinements must not collapse into each other.
|
||||||
|
const _: NegativeInteger = positiveInteger;
|
||||||
|
// @ts-expect-error -- ValidLength is non-negative and must not become a signed integer refinement.
|
||||||
|
const __: NegativeInteger = validLength;
|
||||||
|
|
||||||
|
return negativeInteger;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotUndefinedCheck = (value: string | undefined) => {
|
||||||
|
isAssert.not.undefined(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<string>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotUndefinedUnknownCheck = (value: unknown) => {
|
||||||
|
isAssert.not.undefined(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<undefined>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotUndefinedGenericCheck = <T>(value: T) => {
|
||||||
|
isAssert.not.undefined(value);
|
||||||
|
const _: Exclude<T, undefined> = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const nullValue = null;
|
||||||
|
type Null = typeof nullValue;
|
||||||
|
|
||||||
|
const assertNotNullUnknownCheck = (value: unknown) => {
|
||||||
|
isAssert.not.null(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<Null>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotNullOrUndefinedCheck = (value: string | Null | undefined) => {
|
||||||
|
isAssert.not.nullOrUndefined(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<string>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotNullOrUndefinedUnknownCheck = (value: unknown) => {
|
||||||
|
isAssert.not.nullOrUndefined(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<Null | undefined>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotStringCheck = (value: string | number) => {
|
||||||
|
isAssert.not.string(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<number>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotStringUnknownCheck = (value: unknown) => {
|
||||||
|
isAssert.not.string(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<string>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotStringGenericCheck = <T>(value: T) => {
|
||||||
|
isAssert.not.string(value);
|
||||||
|
const _: Exclude<T, string> = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotBooleanUnknownCheck = (value: unknown) => {
|
||||||
|
isAssert.not.boolean(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<boolean>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotSymbolUnknownCheck = (value: unknown) => {
|
||||||
|
isAssert.not.symbol(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<symbol>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotBigintUnknownCheck = (value: unknown) => {
|
||||||
|
isAssert.not.bigint(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<bigint>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotPrimitiveUnknownCheck = (value: unknown) => {
|
||||||
|
isAssert.not.primitive(value);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
expectTypeOf(value).toEqualTypeOf<object>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotPrimitiveGenericCheck = <T>(value: T) => {
|
||||||
|
isAssert.not.primitive(value);
|
||||||
|
const _: Exclude<T, Primitive> = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotNamedUndefinedExportCheck = (value: 0 | false | '' | Null | undefined | 'ok') => {
|
||||||
|
assertNotUndefined(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<0 | false | '' | Null | 'ok'>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotNamedNullOrUndefinedUnknownExportCheck = (value: unknown) => {
|
||||||
|
assertNotNullOrUndefined(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<Null | undefined>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotNamedStringExportCheck = (value: string | number) => {
|
||||||
|
assertNotString(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<number>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotNamedStringUnknownExportCheck = (value: unknown) => {
|
||||||
|
assertNotString(value);
|
||||||
|
expectTypeOf(value).toEqualTypeOf<UnknownNotPrimitive<string>>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotNamedPrimitiveUnknownExportCheck = (value: unknown) => {
|
||||||
|
assertNotPrimitive(value);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-restricted-types
|
||||||
|
expectTypeOf(value).toEqualTypeOf<object>();
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotCallableDoesNotExistCheck = (value: string | undefined) => {
|
||||||
|
// @ts-expect-error -- Generic negative assertions cannot safely infer complement types from arbitrary predicates.
|
||||||
|
isAssert.not(is.undefined, value);
|
||||||
|
const _: string | undefined = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotNumberDoesNotExistCheck = (value: string | number) => {
|
||||||
|
// @ts-expect-error -- `is.number` rejects `NaN`, so a narrowing negative assertion would be unsound.
|
||||||
|
isAssert.not.number(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||||
|
const _: string | number = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotIntegerDoesNotExistCheck = (value: string | number) => {
|
||||||
|
// @ts-expect-error -- Numeric refinements are intentionally excluded from `assert.not`.
|
||||||
|
isAssert.not.integer(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||||
|
const _: string | number = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotObjectDoesNotExistCheck = (value: Record<string, unknown> | string) => {
|
||||||
|
// @ts-expect-error -- TypeScript's `{}` type includes primitives, so `not.object` cannot safely narrow every object-like input.
|
||||||
|
isAssert.not.object(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||||
|
const _: Record<string, unknown> | string = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotBlobDoesNotExistCheck = (value: Blob | File | string) => {
|
||||||
|
// @ts-expect-error -- `File` extends `Blob` in TypeScript but does not match the exact runtime `Blob` check.
|
||||||
|
isAssert.not.blob(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||||
|
const _: Blob | File | string = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotMapDoesNotExistCheck = (value: Map<string, number> | string) => {
|
||||||
|
// @ts-expect-error -- Structural object types such as `Map` can be assignable in TypeScript without matching the runtime brand check.
|
||||||
|
isAssert.not.map(value); // eslint-disable-line @typescript-eslint/no-unsafe-call, unicorn/no-array-callback-reference
|
||||||
|
const _: Map<string, number> | string = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotSetDoesNotExistCheck = (value: Set<string> | string) => {
|
||||||
|
// @ts-expect-error -- Structural object types such as `Set` can be assignable in TypeScript without matching the runtime brand check.
|
||||||
|
isAssert.not.set(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||||
|
const _: Set<string> | string = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const assertNotDateDoesNotExistCheck = (value: Date | string) => {
|
||||||
|
// @ts-expect-error -- Structural object types such as `Date` can be assignable in TypeScript without matching the runtime brand check.
|
||||||
|
isAssert.not.date(value); // eslint-disable-line @typescript-eslint/no-unsafe-call
|
||||||
|
const _: Date | string = value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Suppress unused variable warnings
|
||||||
|
nanCheck(42);
|
||||||
|
finiteNumberCheck(42);
|
||||||
|
nonNegativeNumberCheck(42);
|
||||||
|
positiveIntegerCheck(42);
|
||||||
|
negativeIntegerCheck(-1);
|
||||||
|
nonNegativeIntegerCheck(0);
|
||||||
|
infiniteCheck(Number.POSITIVE_INFINITY);
|
||||||
|
integerCheck(1);
|
||||||
|
safeIntegerCheck(1);
|
||||||
|
evenIntegerCheck(2);
|
||||||
|
oddIntegerCheck(1);
|
||||||
|
positiveNumberCheck(1);
|
||||||
|
negativeNumberCheck(-1);
|
||||||
|
validLengthCheck(0);
|
||||||
|
integerUnknownCheck(1);
|
||||||
|
positiveIntegerUnknownCheck(1);
|
||||||
|
integerMixedUnionCheck(1);
|
||||||
|
positiveNumberMixedUnionCheck(1);
|
||||||
|
chainedNumericGuardCheck(1);
|
||||||
|
distinctNumericBrandsStayDistinct(42 as PositiveInteger, -1 as NegativeInteger, 0 as ValidLength);
|
||||||
|
assertNotUndefinedCheck('🦄');
|
||||||
|
assertNotUndefinedUnknownCheck('🦄');
|
||||||
|
assertNotUndefinedGenericCheck<string | undefined>('🦄');
|
||||||
|
assertNotNullUnknownCheck('🦄');
|
||||||
|
assertNotNullOrUndefinedCheck('🦄');
|
||||||
|
assertNotNullOrUndefinedUnknownCheck('🦄');
|
||||||
|
assertNotStringCheck(1);
|
||||||
|
assertNotStringUnknownCheck(1);
|
||||||
|
assertNotStringGenericCheck<string | number>(1);
|
||||||
|
assertNotBooleanUnknownCheck(1);
|
||||||
|
assertNotSymbolUnknownCheck(1);
|
||||||
|
assertNotBigintUnknownCheck(1);
|
||||||
|
assertNotPrimitiveUnknownCheck({});
|
||||||
|
assertNotPrimitiveGenericCheck<string | {unicorn: true}>({unicorn: true});
|
||||||
|
assertNotNamedUndefinedExportCheck(0);
|
||||||
|
assertNotNamedNullOrUndefinedUnknownExportCheck('🦄');
|
||||||
|
assertNotNamedStringExportCheck(1);
|
||||||
|
assertNotNamedStringUnknownExportCheck(1);
|
||||||
|
assertNotNamedPrimitiveUnknownExportCheck({});
|
||||||
|
assertNotCallableDoesNotExistCheck('🦄');
|
||||||
|
assertNotNumberDoesNotExistCheck(Number.NaN);
|
||||||
|
assertNotIntegerDoesNotExistCheck(1.5);
|
||||||
|
assertNotObjectDoesNotExistCheck('🦄');
|
||||||
|
assertNotBlobDoesNotExistCheck('🦄');
|
||||||
|
assertNotMapDoesNotExistCheck('🦄');
|
||||||
|
assertNotSetDoesNotExistCheck('🦄');
|
||||||
|
assertNotDateDoesNotExistCheck('🦄');
|
||||||
|
|
@ -1,5 +1,11 @@
|
||||||
{
|
{
|
||||||
"extends": "@sindresorhus/tsconfig",
|
"extends": "@sindresorhus/tsconfig",
|
||||||
|
"compilerOptions": {
|
||||||
|
"types": ["node"],
|
||||||
|
"rootDir": "source",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"rewriteRelativeImportExtensions": true
|
||||||
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"source"
|
"source"
|
||||||
],
|
],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue