parent
48df5c429c
commit
2d4956e634
4 changed files with 374 additions and 6 deletions
38
readme.md
38
readme.md
|
|
@ -786,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`.
|
||||||
|
|
|
||||||
|
|
@ -403,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)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -983,9 +987,7 @@ export function isWhitespaceString(value: unknown): value is Whitespace {
|
||||||
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');
|
||||||
|
|
@ -998,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));
|
||||||
|
|
@ -1123,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;
|
||||||
|
|
@ -1135,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,
|
||||||
|
|
|
||||||
81
test/test.ts
81
test/test.ts
|
|
@ -466,6 +466,17 @@ const subClasses = new Map<TypeNameWithFixture, TypeNameWithFixture[]>([
|
||||||
['object', keysOf(objectTypes)],
|
['object', keysOf(objectTypes)],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const notAssertionFixtures = {
|
||||||
|
bigint: {fixture: 1n, nonFixture: '🦄', typeDescription: 'bigint'},
|
||||||
|
boolean: {fixture: false, nonFixture: '🦄', typeDescription: 'boolean'},
|
||||||
|
null: {fixture: null, nonFixture: '🦄', typeDescription: 'null'},
|
||||||
|
nullOrUndefined: {fixtures: [null, undefined], nonFixture: '🦄', typeDescription: 'null or undefined'},
|
||||||
|
primitive: {fixtures: [false, null, undefined], nonFixture: [], typeDescription: 'primitive'},
|
||||||
|
string: {fixture: '🦄', nonFixture: 1, typeDescription: 'string'},
|
||||||
|
symbol: {fixture: Symbol('🦄'), nonFixture: '🦄', typeDescription: 'symbol'},
|
||||||
|
undefined: {fixture: undefined, nonFixture: null, typeDescription: 'undefined'},
|
||||||
|
} as const satisfies Record<keyof typeof isAssert.not, ({fixture: unknown} | {fixtures: unknown[]}) & {nonFixture: unknown; typeDescription: string}>;
|
||||||
|
|
||||||
// This ensures a certain method matches only the types it's supposed to and none of the other methods' types
|
// This ensures a certain method matches only the types it's supposed to and none of the other methods' types
|
||||||
for (const type of keysOf(types)) {
|
for (const type of keysOf(types)) {
|
||||||
test(`is.${type}`, () => {
|
test(`is.${type}`, () => {
|
||||||
|
|
@ -2534,6 +2545,14 @@ test('custom assertion message', () => {
|
||||||
isAssert.nativePromise(undefined, message);
|
isAssert.nativePromise(undefined, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
assertThrowsTypeErrorWithMessage(() => {
|
||||||
|
isAssert.not.undefined(undefined, message);
|
||||||
|
});
|
||||||
|
|
||||||
|
assertThrowsTypeErrorWithMessage(() => {
|
||||||
|
isAssert.not.string('hello', message);
|
||||||
|
});
|
||||||
|
|
||||||
assertThrowsTypeErrorWithMessage(() => {
|
assertThrowsTypeErrorWithMessage(() => {
|
||||||
isAssert.negativeNumber(undefined, message);
|
isAssert.negativeNumber(undefined, message);
|
||||||
});
|
});
|
||||||
|
|
@ -2715,6 +2734,68 @@ test('custom assertion message', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('isAssert.not.undefined', () => {
|
||||||
|
assert.throws(() => {
|
||||||
|
isAssert.not.undefined(undefined);
|
||||||
|
}, {
|
||||||
|
message: 'Expected value which is not `undefined`, received value of type `undefined`.',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.not.undefined(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.not.undefined(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.not.undefined(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.not.undefined('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isAssert.not', () => {
|
||||||
|
assert.deepStrictEqual(new Set(keysOf(isAssert.not)), new Set(keysOf(notAssertionFixtures)));
|
||||||
|
|
||||||
|
for (const type of keysOf(notAssertionFixtures)) {
|
||||||
|
const {nonFixture, typeDescription} = notAssertionFixtures[type];
|
||||||
|
const testAssert = isAssert.not[type];
|
||||||
|
const fixtures = 'fixtures' in notAssertionFixtures[type] ? notAssertionFixtures[type].fixtures : [notAssertionFixtures[type].fixture];
|
||||||
|
|
||||||
|
for (const fixture of fixtures) {
|
||||||
|
assert.throws(() => {
|
||||||
|
testAssert(fixture);
|
||||||
|
}, {
|
||||||
|
message: `Expected value which is not \`${typeDescription}\`, received value of type \`${is(fixture)}\`.`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
testAssert(nonFixture);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.strictEqual('number' in isAssert.not, false);
|
||||||
|
assert.strictEqual('integer' in isAssert.not, false);
|
||||||
|
assert.strictEqual('object' in isAssert.not, false);
|
||||||
|
assert.strictEqual('blob' in isAssert.not, false);
|
||||||
|
assert.strictEqual('array' in isAssert.not, false);
|
||||||
|
assert.strictEqual('date' in isAssert.not, false);
|
||||||
|
assert.strictEqual('function' in isAssert.not, false);
|
||||||
|
assert.strictEqual('map' in isAssert.not, false);
|
||||||
|
assert.strictEqual('set' in isAssert.not, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('isAssert.not edge cases', () => {
|
||||||
|
assert.doesNotThrow(() => {
|
||||||
|
isAssert.not.null(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('is.optional', () => {
|
test('is.optional', () => {
|
||||||
assert.ok(is.optional(undefined, is.string));
|
assert.ok(is.optional(undefined, is.string));
|
||||||
assert.ok(is.optional('🦄', is.string));
|
assert.ok(is.optional('🦄', is.string));
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,10 @@
|
||||||
|
import {expectTypeOf} from 'expect-type';
|
||||||
import is, {
|
import is, {
|
||||||
|
assert as isAssert,
|
||||||
|
assertNotNullOrUndefined,
|
||||||
|
assertNotPrimitive,
|
||||||
|
assertNotString,
|
||||||
|
assertNotUndefined,
|
||||||
type EvenInteger,
|
type EvenInteger,
|
||||||
type FiniteNumber,
|
type FiniteNumber,
|
||||||
type Integer,
|
type Integer,
|
||||||
|
|
@ -12,10 +18,14 @@ import is, {
|
||||||
type PositiveInfinity,
|
type PositiveInfinity,
|
||||||
type PositiveInteger,
|
type PositiveInteger,
|
||||||
type PositiveNumber,
|
type PositiveNumber,
|
||||||
|
type Primitive,
|
||||||
type SafeInteger,
|
type SafeInteger,
|
||||||
type ValidLength,
|
type ValidLength,
|
||||||
} from '../source/index.ts';
|
} 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:
|
// For each predicate, verify two things:
|
||||||
// 1. True branch narrows to the branded type.
|
// 1. True branch narrows to the branded type.
|
||||||
// 2. False branch on a `number` input stays `number` (not `never`).
|
// 2. False branch on a `number` input stays `number` (not `never`).
|
||||||
|
|
@ -197,6 +207,154 @@ const distinctNumericBrandsStayDistinct = (
|
||||||
return negativeInteger;
|
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
|
// Suppress unused variable warnings
|
||||||
nanCheck(42);
|
nanCheck(42);
|
||||||
finiteNumberCheck(42);
|
finiteNumberCheck(42);
|
||||||
|
|
@ -218,3 +376,30 @@ integerMixedUnionCheck(1);
|
||||||
positiveNumberMixedUnionCheck(1);
|
positiveNumberMixedUnionCheck(1);
|
||||||
chainedNumericGuardCheck(1);
|
chainedNumericGuardCheck(1);
|
||||||
distinctNumericBrandsStayDistinct(42 as PositiveInteger, -1 as NegativeInteger, 0 as ValidLength);
|
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('🦄');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue