Compare commits

..

33 commits
v6.3.0 ... main

Author SHA1 Message Date
Sindre Sorhus
7821031c66 Fix CI 2026-05-11 22:57:22 +09:00
Sindre Sorhus
a439305554 8.1.0 2026-05-11 20:22:19 +09:00
Sindre Sorhus
2d4956e634 Add negative assertion helper
Fixes #220
2026-05-11 20:17:16 +09:00
Sindre Sorhus
48df5c429c 8.0.0 2026-04-09 22:09:01 +07:00
Sindre Sorhus
13febb6b01 Fix some type guards 2026-04-09 14:56:49 +07:00
Sindre Sorhus
cb4ee0e92c Fix isEnumCase incorrectly accepting numeric enum key names 2026-04-09 14:17:12 +07:00
Sindre Sorhus
54fc09406a Add negativeInteger, nonNegativeInteger, arrayOf, and oneOf predicates 2026-04-08 19:13:51 +07:00
Sindre Sorhus
63be5c0c19 Add finiteNumber, nonNegativeNumber, and positiveInteger predicates 2026-04-08 16:26:44 +07:00
Sindre Sorhus
ac46b5400d Fix isInRange silently returning false when range contains NaN 2026-04-08 15:09:53 +07:00
Sindre Sorhus
47415dc46a Fix isNumericString incorrectly accepting strings with surrounding whitespace 2026-04-08 05:51:14 +07:00
Sindre Sorhus
3b40955b02 Fix handling of functions and arrays in isEmptyObject and isNonEmptyObject 2026-04-08 05:51:13 +07:00
Sindre Sorhus
faf700367e Require Node.js 22 2026-04-08 04:38:52 +07:00
Sindre Sorhus
eff8e6b318 7.2.0 2025-12-27 11:35:17 +01:00
Sindre Sorhus
9bdcd9b57f Add predicate factory mode to is.any and is.all
Fixes #218
2025-12-27 11:33:25 +01:00
Sindre Sorhus
fbcc68e139 7.1.1 2025-11-01 00:20:45 +07:00
Tim Griesser
e7c84fcb79
Fix is.class for minified class expression (#217) 2025-11-01 00:18:25 +07:00
Sindre Sorhus
d22ab62991 7.1.0 2025-09-14 02:05:33 +07:00
Sindre Sorhus
1f2440ae0d Add is.optional and assert.optional
Fixes #111
2025-09-13 15:48:57 +07:00
Sindre Sorhus
c68ad76062 Fix TypeScript type narrowing issue with isUrlString
Fixes #212
2025-09-12 05:08:52 +07:00
Bjorn Stromberg
ef35cc350a
Structure fixtures so they can be easily tested for exclusivity (#215) 2025-06-06 13:58:46 +02:00
Sindre Sorhus
882a91c54f 7.0.2 2025-06-04 12:57:42 +03:00
Sindre Sorhus
e8e8124ba7 FIx observable checking
Fixes #214
2025-06-04 12:55:44 +03:00
Sindre Sorhus
47a5099325 Minor tweaks 2025-06-04 12:12:42 +03:00
Sindre Sorhus
e0976457e0 7.0.1 2024-09-06 22:54:53 +07:00
Martin Eneqvist
5565c5e3ba
Fix passing in assertion message to assertArray (#210)
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
2024-09-06 22:52:56 +07:00
Sindre Sorhus
ab85d9bca9 7.0.0 2024-07-10 15:43:01 +02:00
Sindre Sorhus
0ff273fee8 Require Node.js 18 2024-07-10 15:41:12 +02:00
Martin Eneqvist
25a376875d
Fix type guard for isWhitespaceString and isEmptyStringOrWhitespace (#207) 2024-06-26 14:31:56 +02:00
Bjorn Stromberg
8cbcaee674
Remove deprecated methods and improve Class definition (#209) 2024-06-26 14:30:00 +02:00
Bjorn Stromberg
92699e1049
Replace ts-node with tsimp (#208) 2024-06-25 02:08:48 +02:00
Sindre Sorhus
47f49741ea 6.3.1 2024-05-16 11:15:23 +03:00
Sindre Sorhus
0df21e4151 Add missing type guard for is.enumCase
Fixes #205
2024-05-16 11:11:44 +03:00
Sindre Sorhus
f7148e19dc Meta tweaks 2024-04-29 01:32:06 +07:00
13 changed files with 3146 additions and 1813 deletions

View file

@ -10,10 +10,11 @@ jobs:
fail-fast: false
matrix:
node-version:
- 20
- 24
- 22
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node-version }}
- run: npm install

3
.gitignore vendored
View file

@ -1,3 +1,4 @@
node_modules
yarn.lock
dist
/distribution
.tsimp

9
AGENTS.md Normal file
View 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
View file

@ -0,0 +1 @@
AGENTS.md

View file

@ -1,6 +1,6 @@
{
"name": "@sindresorhus/is",
"version": "6.3.0",
"version": "8.1.0",
"description": "Type check values",
"license": "MIT",
"repository": "sindresorhus/is",
@ -11,19 +11,21 @@
"url": "https://sindresorhus.com"
},
"type": "module",
"exports": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"types": "./distribution/index.d.ts",
"default": "./distribution/index.js"
},
"sideEffects": false,
"engines": {
"node": ">=16"
"node": ">=22"
},
"scripts": {
"build": "del dist && tsc",
"test": "tsc --noEmit && xo && NODE_OPTIONS='--loader=ts-node/esm --no-warnings=ExperimentalWarning' ava",
"build": "del distribution && tsc",
"test": "tsc --noEmit && tsc --project test/tsconfig.json --noEmit && xo && node --experimental-transform-types --test test/test.ts",
"prepare": "npm run build"
},
"files": [
"dist"
"distribution"
],
"keywords": [
"type",
@ -49,25 +51,26 @@
"typeguards",
"types"
],
"devDependencies": {
"@sindresorhus/tsconfig": "^4.0.0",
"@types/jsdom": "^21.1.1",
"@types/node": "^20.5.0",
"@types/zen-observable": "^0.8.3",
"ava": "^5.3.1",
"del-cli": "^5.0.0",
"jsdom": "^22.1.0",
"rxjs": "^7.8.1",
"tempy": "^3.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.6",
"xo": "^0.56.0",
"zen-observable": "^0.10.0",
"expect-type": "^0.16.0"
},
"ava": {
"extensions": {
"ts": "module"
"xo": {
"rules": {
"@typescript-eslint/no-unsafe-enum-comparison": "off",
"@typescript-eslint/no-confusing-void-expression": "off",
"@typescript-eslint/no-unsafe-type-assertion": "off",
"@stylistic/operator-linebreak": "off"
}
},
"devDependencies": {
"@sindresorhus/tsconfig": "^8.1.0",
"@types/jsdom": "^28.0.1",
"@types/node": "^25.5.2",
"@types/zen-observable": "^0.8.7",
"del-cli": "^7.0.0",
"expect-type": "^1.3.0",
"jsdom": "^29.0.2",
"rxjs": "^7.8.2",
"tempy": "^3.2.0",
"typescript": "6.0.2",
"xo": "^2.0.2",
"zen-observable": "^0.10.0"
}
}

165
readme.md
View file

@ -130,9 +130,24 @@ 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.
```
##### .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)
##### .buffer(value)
> [!NOTE]
> [Prefer using `Uint8Array` instead of `Buffer`.](https://sindresorhus.com/blog/goodbye-nodejs-buffer)
##### .blob(value)
##### .object(value)
@ -459,6 +474,10 @@ function foo() {
foo();
```
##### .finiteNumber(value)
Check if `value` is a number and is finite. Excludes `Infinity` and `-Infinity`.
##### .positiveNumber(value)
Check if `value` is a number and is more than 0.
@ -467,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.
##### .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)
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.
@ -571,6 +606,31 @@ is.any([is.boolean, is.number], 'unicorns', [], new Map());
//=> false
```
##### .any(predicate[])
Using an array of `predicate[]` without values, returns a combined type guard that checks if a value matches **any** of the predicates:
```js
const isStringOrNumber = is.any([is.string, is.number]);
isStringOrNumber('hello');
//=> true
isStringOrNumber(123);
//=> true
isStringOrNumber(true);
//=> false
```
This is useful for composing with other methods like `is.optional`:
```js
is.optional(value, is.any([is.string, is.number]));
```
An empty predicate array currently returns a predicate that always returns `false`. This will throw in the next major release.
##### .all(predicate, ...values)
Returns `true` if **all** of the input `values` returns true in the `predicate`:
@ -583,6 +643,54 @@ is.all(is.string, '🦄', [], 'unicorns');
//=> false
```
##### .all(predicate[])
Using an array of `predicate[]` without values, returns a combined type guard that checks if a value matches **all** of the predicates:
```js
const isArrayAndNonEmpty = is.all([is.array, is.nonEmptyArray]);
isArrayAndNonEmpty(['hello']);
//=> true
isArrayAndNonEmpty([]);
//=> false
```
This is useful for composing with other methods like `is.optional`:
```js
is.optional(value, is.all([is.object, is.plainObject]));
```
An empty predicate array currently returns a predicate that always returns `true`. This will throw in the next major release.
##### .optional(value, predicate)
Returns `true` if `value` is `undefined` or satisfies the given `predicate`.
```js
is.optional(undefined, is.string);
//=> true
is.optional('🦄', is.string);
//=> true
is.optional(123, is.string);
//=> 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)
Returns `true` if the value is a valid date.
@ -678,6 +786,61 @@ handleMovieRatingApiResponse({rating: 0.87, title: 'The Matrix'});
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
Asserts that `value` is `undefined` or satisfies the provided `assertion`.
```ts
import {assert} from '@sindresorhus/is';
assert.optional(undefined, assert.string);
// Passes without throwing
assert.optional('🦄', assert.string);
// Passes without throwing
assert.optional(123, assert.string);
// Throws: Expected value which is `string`, received value of type `number`
```
## Generic type parameters
The type guards and type assertions are aware of [generic type parameters](https://www.typescriptlang.org/docs/handbook/generics.html), such as `Promise<T>` and `Map<Key, Value>`. The default is `unknown` for most cases, since `is` cannot check them at runtime. If the generic type is known at compile-time, either implicitly (inferred) or explicitly (provided), `is` propagates the type so it can be used later.
@ -743,7 +906,7 @@ The most common mistakes I noticed in these modules was using `instanceof` for t
## Related
- [ow](https://github.com/sindresorhus/ow) - Function argument validation for humans
- [environment](https://github.com/sindresorhus/environment) - Check which JavaScript environment your code is running in at runtime
- [is-stream](https://github.com/sindresorhus/is-stream) - Check if something is a Node.js stream
- [is-observable](https://github.com/sindresorhus/is-observable) - Check if a value is an Observable
- [file-type](https://github.com/sindresorhus/file-type) - Detect the file type of a Buffer/Uint8Array

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,8 @@
Matches any [primitive value](https://developer.mozilla.org/en-US/docs/Glossary/Primitive).
*/
export type Primitive =
| null // eslint-disable-line @typescript-eslint/ban-types
// eslint-disable-next-line @typescript-eslint/no-restricted-types
| null
| undefined
| string
| number
@ -15,14 +16,12 @@ export type Primitive =
/**
Matches a [`class` constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes).
*/
/// type Constructor<T, Arguments extends unknown[] = any[]> = new(...arguments_: Arguments) => T;
type Constructor<T, Arguments extends unknown[] = any[]> = new(...arguments_: Arguments) => T;
/**
Matches a [`class`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes).
*/
// TODO: Use the below in the next major version.
// export type Class<T, Arguments extends unknown[] = any[]> = Constructor<T, Arguments> & {prototype: T};
export type Class<T = unknown, Arguments extends any[] = any[]> = new (...arguments_: Arguments) => T;
export type Class<T, Arguments extends unknown[] = any[]> = Constructor<T, Arguments> & {prototype: T};
/**
Matches any [typed array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray), like `Uint8Array` or `Float64Array`.
@ -55,10 +54,11 @@ export type 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 WeakRef<T extends object> = { // eslint-disable-line @typescript-eslint/ban-types
// eslint-disable-next-line @typescript-eslint/no-restricted-types
export type WeakRef<T extends object> = {
readonly [Symbol.toStringTag]: 'WeakRef';
deref(): T | undefined;
};
@ -75,3 +75,125 @@ export type NodeStream = {
export type Predicate = (value: unknown) => boolean;
export type NonEmptyString = string & {0: string};
export type Whitespace = ' ';
type Brand<Key extends string> = Readonly<Record<Key, true>>;
/**
A string that represents a valid URL.
This is a branded type to prevent incorrect TypeScript type narrowing.
*/
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'>;

3
source/utilities.ts Normal file
View file

@ -0,0 +1,3 @@
export function keysOf<T extends Record<PropertyKey, unknown>>(value: T): Array<keyof T> {
return Object.keys(value) as Array<keyof T>; // eslint-disable-line @typescript-eslint/no-unnecessary-type-assertion
}

File diff suppressed because it is too large Load diff

12
test/tsconfig.json Normal file
View 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
View 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('🦄');

View file

@ -1,14 +1,12 @@
{
"extends": "@sindresorhus/tsconfig",
"compilerOptions": {
"outDir": "dist",
"types": ["node"],
"rootDir": "source",
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true
},
"include": [
"source"
],
"ts-node": {
"transpileOnly": true,
"files": true,
"experimentalResolver": true
}
}