From c842cc260f6d773bf4dc800ebebc47dd026cab0a Mon Sep 17 00:00:00 2001 From: Joel Purra Date: Tue, 21 Jan 2020 17:56:44 +0100 Subject: [PATCH 1/3] Upgrade dependencies (#101) --- package.json | 13 ++++++++----- source/index.ts | 2 +- test/test.ts | 2 +- tsconfig.xo.json | 6 ++++++ 4 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 tsconfig.xo.json diff --git a/package.json b/package.json index 1bb1f91..5867b4f 100644 --- a/package.json +++ b/package.json @@ -46,20 +46,20 @@ "types" ], "devDependencies": { - "@sindresorhus/tsconfig": "^0.6.0", + "@sindresorhus/tsconfig": "^0.7.0", "@types/jsdom": "^12.2.4", "@types/node": "^12.12.6", "@types/zen-observable": "^0.8.0", - "@typescript-eslint/eslint-plugin": "^1.11.0", - "@typescript-eslint/parser": "^1.11.0", + "@typescript-eslint/eslint-plugin": "^2.17.0", + "@typescript-eslint/parser": "^2.17.0", "ava": "^2.1.0", "del-cli": "^2.0.0", - "eslint-config-xo-typescript": "^0.15.0", + "eslint-config-xo-typescript": "^0.24.1", "jsdom": "^15.0.0", "rxjs": "^6.4.0", "tempy": "^0.3.0", "ts-node": "^8.3.0", - "typescript": "^3.7.2", + "typescript": "^3.7.5", "xo": "^0.25.3", "zen-observable": "^0.8.8" }, @@ -80,6 +80,9 @@ "extensions": [ "ts" ], + "parserOptions": { + "project": "./tsconfig.xo.json" + }, "globals": [ "BigInt", "BigInt64Array", diff --git a/source/index.ts b/source/index.ts index 6cb8159..94ba314 100644 --- a/source/index.ts +++ b/source/index.ts @@ -278,8 +278,8 @@ is.typedArray = (value: unknown): value is TypedArray => { }; export interface ArrayLike { - readonly length: number; readonly [index: number]: T; + readonly length: number; } const isValidLength = (value: unknown): value is number => is.safeInteger(value) && value >= 0; diff --git a/test/test.ts b/test/test.ts index 0d9c13a..b7d017d 100644 --- a/test/test.ts +++ b/test/test.ts @@ -457,7 +457,7 @@ const testType = (t: ExecutionContext, type: string, exclude?: string[]) => { for (const [key, {fixtures}] of types) { // TODO: Automatically exclude value types in other tests that we have in the current one. // Could reduce the use of `exclude`. - if (exclude && exclude.includes(key)) { + if (exclude?.includes(key)) { continue; } diff --git a/tsconfig.xo.json b/tsconfig.xo.json new file mode 100644 index 0000000..b01049f --- /dev/null +++ b/tsconfig.xo.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.json", + "include": [ + "test" + ] +} From 0c3f11038638920377c44b0832ad0a89088bfe16 Mon Sep 17 00:00:00 2001 From: Joel Purra Date: Wed, 22 Jan 2020 12:08:35 +0100 Subject: [PATCH 2/3] Add assertion type guards (#97) --- readme.md | 34 +++++ source/index.ts | 237 +++++++++++++++++++++++++++++++++ test/test.ts | 347 ++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 593 insertions(+), 25 deletions(-) diff --git a/readme.md b/readme.md index bb744e7..2d55818 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,7 @@ For example, `is.string('🦄') //=> true` - Written in TypeScript - [Extensive use of type guards](#type-guards) +- [Supports type assertions](#type-assertions) - Actively maintained - 2 million weekly downloads @@ -37,6 +38,15 @@ is.number(6); //=> true ``` +[Assertions](#type-assertions) perform the same type checks, but throw errors if the type does not match. + +```js +const {assert} = require('@sindresorhus/is'); + +assert.string(foo); +// foo is known to be a string here +``` + ## API @@ -425,6 +435,30 @@ padLeft('🦄', '🌈'); //=> '🌈🦄' ``` +## Type assertions + +The type guards are also available as [typescript assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions), which throw an error for unexpected types. It is a convenient one-line version of the often repetitive "if-not-expected-type-throw" pattern. + +```ts +import {assert} from '@sindresorhus/is'; + +const handleMovieRatingApiResponse = (response: unknown) => { + assert.plainObject(response); + // `response` is typed as a plain `object` with `unknown` properties + assert.number(response.rating); + // `response.rating` is typed as a `number` + assert.string(response.title); + // `response.title` is typed as a `string` + + return `${response.title} (${response.rating * 10})`; +}; + +handleMovieRatingApiResponse({rating: 0.87, title: 'The Matrix'}); +//=> 'The Matrix (8.7)' + +handleMovieRatingApiResponse({status: 'system maintenance'}); +//=> throws error +``` ## FAQ diff --git a/source/index.ts b/source/index.ts index 94ba314..36004d2 100644 --- a/source/index.ts +++ b/source/index.ts @@ -385,6 +385,231 @@ const predicateOnArray = (method: ArrayMethod, predicate: Predicate, values: unk is.any = (predicate: Predicate, ...values: unknown[]): boolean => predicateOnArray(Array.prototype.some, predicate, values); is.all = (predicate: Predicate, ...values: unknown[]): boolean => predicateOnArray(Array.prototype.every, predicate, values); +const assertType = (condition: boolean, description: string, value: unknown): asserts condition => { + if (!condition) { + throw new TypeError(`Expected value which is "${description}", received value of type ${is(value)}.`); + } +}; + +export const enum AssertionTypeDescription { + class_ = 'Class', + numericString = 'string with a number', + nullOrUndefined = 'null or undefined', + iterable = 'Iterable', + asyncIterable = 'AsyncIterable', + nativePromise = 'native Promise', + urlString = 'string with a URL', + truthy = 'truthy', + falsy = 'falsy', + nan = 'NaN', + primitive = 'primitive', + integer = 'integer', + safeInteger = 'integer', + plainObject = 'plain object', + arrayLike = 'array-like', + typedArray = 'TypedArray', + domElement = 'Element', + nodeStream = 'Node.js Stream', + infinite = 'infinite number', + emptyArray = 'empty array', + nonEmptyArray = 'non-empty array', + emptyString = 'empty string', + nonEmptyString = 'non-empty string', + emptyStringOrWhitespace = 'empty string or whitespace', + emptyObject = 'empty object', + nonEmptyObject = 'non-empty object', + emptySet = 'empty set', + nonEmptySet = 'non-empty set', + emptyMap = 'empty map', + nonEmptyMap = 'non-empty map', + + evenInteger = 'even integer', + oddInteger = 'odd integer', + + directInstanceOf = 'T', + inRange = 'in range', + + any = 'predicate returns truthy for any value', + all = 'predicate returns truthy for all values', +} + +// Type assertions have to be declared with an explicit type. +interface Assert { + // Unknowns. + undefined: (value: unknown) => asserts value is undefined; + string: (value: unknown) => asserts value is string; + number: (value: unknown) => asserts value is number; + bigint: (value: unknown) => asserts value is bigint; + // eslint-disable-next-line @typescript-eslint/ban-types + function_: (value: unknown) => asserts value is Function; + null_: (value: unknown) => asserts value is null; + class_: (value: unknown) => asserts value is Class; + boolean: (value: unknown) => asserts value is boolean; + symbol: (value: unknown) => asserts value is symbol; + numericString: (value: unknown) => asserts value is string; + array: (value: unknown) => asserts value is T[]; + buffer: (value: unknown) => asserts value is Buffer; + nullOrUndefined: (value: unknown) => asserts value is null | undefined; + object: (value: unknown) => asserts value is Record; + iterable: (value: unknown) => asserts value is Iterable; + asyncIterable: (value: unknown) => asserts value is AsyncIterable; + generator: (value: unknown) => asserts value is Generator; + nativePromise: (value: unknown) => asserts value is Promise; + promise: (value: unknown) => asserts value is Promise; + generatorFunction: (value: unknown) => asserts value is GeneratorFunction; + // eslint-disable-next-line @typescript-eslint/ban-types + asyncFunction: (value: unknown) => asserts value is Function; + // eslint-disable-next-line @typescript-eslint/ban-types + boundFunction: (value: unknown) => asserts value is Function; + regExp: (value: unknown) => asserts value is RegExp; + date: (value: unknown) => asserts value is Date; + error: (value: unknown) => asserts value is Error; + map: (value: unknown) => asserts value is Map; + set: (value: unknown) => asserts value is Set; + weakMap: (value: unknown) => asserts value is WeakMap; + weakSet: (value: unknown) => asserts value is WeakSet; + int8Array: (value: unknown) => asserts value is Int8Array; + uint8Array: (value: unknown) => asserts value is Uint8Array; + uint8ClampedArray: (value: unknown) => asserts value is Uint8ClampedArray; + int16Array: (value: unknown) => asserts value is Int16Array; + uint16Array: (value: unknown) => asserts value is Uint16Array; + int32Array: (value: unknown) => asserts value is Int32Array; + uint32Array: (value: unknown) => asserts value is Uint32Array; + float32Array: (value: unknown) => asserts value is Float32Array; + float64Array: (value: unknown) => asserts value is Float64Array; + bigInt64Array: (value: unknown) => asserts value is BigInt64Array; + bigUint64Array: (value: unknown) => asserts value is BigUint64Array; + arrayBuffer: (value: unknown) => asserts value is ArrayBuffer; + sharedArrayBuffer: (value: unknown) => asserts value is SharedArrayBuffer; + dataView: (value: unknown) => asserts value is DataView; + urlInstance: (value: unknown) => asserts value is URL; + urlString: (value: unknown) => asserts value is string; + truthy: (value: unknown) => asserts value is unknown; + falsy: (value: unknown) => asserts value is unknown; + nan: (value: unknown) => asserts value is unknown; + primitive: (value: unknown) => asserts value is Primitive; + integer: (value: unknown) => asserts value is number; + safeInteger: (value: unknown) => asserts value is number; + plainObject: (value: unknown) => asserts value is {[key: string]: unknown}; + typedArray: (value: unknown) => asserts value is TypedArray; + arrayLike: (value: unknown) => asserts value is ArrayLike; + domElement: (value: unknown) => asserts value is Element; + observable: (value: unknown) => asserts value is ObservableLike; + nodeStream: (value: unknown) => asserts value is NodeStream; + infinite: (value: unknown) => asserts value is number; + emptyArray: (value: unknown) => asserts value is never[]; + nonEmptyArray: (value: unknown) => asserts value is unknown[]; + emptyString: (value: unknown) => asserts value is ''; + nonEmptyString: (value: unknown) => asserts value is string; + emptyStringOrWhitespace: (value: unknown) => asserts value is string; + emptyObject: (value: unknown) => asserts value is {[key: string]: never}; + nonEmptyObject: (value: unknown) => asserts value is {[key: string]: unknown}; + emptySet: (value: unknown) => asserts value is Set; + nonEmptySet: (value: unknown) => asserts value is Set; + emptyMap: (value: unknown) => asserts value is Map; + nonEmptyMap: (value: unknown) => asserts value is Map; + + // Numbers. + evenInteger: (value: number) => asserts value is number; + oddInteger: (value: number) => asserts value is number; + + // Two arguments. + directInstanceOf: (instance: unknown, class_: Class) => asserts instance is T; + inRange: (value: number, range: number | number[]) => asserts value is number; + + // Variadic functions. + any: (predicate: Predicate, ...values: unknown[]) => void | never; + all: (predicate: Predicate, ...values: unknown[]) => void | never; +} + +export const assert: Assert = { + // Unknowns. + undefined: (value: unknown): asserts value is undefined => assertType(is.undefined(value), TypeName.undefined, value), + string: (value: unknown): asserts value is string => assertType(is.string(value), TypeName.string, value), + number: (value: unknown): asserts value is number => assertType(is.number(value), TypeName.number, value), + bigint: (value: unknown): asserts value is bigint => assertType(is.bigint(value), TypeName.bigint, value), + // eslint-disable-next-line @typescript-eslint/ban-types + function_: (value: unknown): asserts value is Function => assertType(is.function_(value), TypeName.Function, value), + null_: (value: unknown): asserts value is null => assertType(is.null_(value), TypeName.null, value), + class_: (value: unknown): asserts value is Class => assertType(is.class_(value), AssertionTypeDescription.class_, value), + boolean: (value: unknown): asserts value is boolean => assertType(is.boolean(value), TypeName.boolean, value), + symbol: (value: unknown): asserts value is symbol => assertType(is.symbol(value), TypeName.symbol, value), + numericString: (value: unknown): asserts value is string => assertType(is.numericString(value), AssertionTypeDescription.numericString, value), + array: (value: unknown): asserts value is T[] => assertType(is.array(value), TypeName.Array, value), + buffer: (value: unknown): asserts value is Buffer => assertType(is.buffer(value), TypeName.Buffer, value), + nullOrUndefined: (value: unknown): asserts value is null | undefined => assertType(is.nullOrUndefined(value), AssertionTypeDescription.nullOrUndefined, value), + object: (value: unknown): asserts value is Record => assertType(is.object(value), TypeName.Object, value), + iterable: (value: unknown): asserts value is Iterable => assertType(is.iterable(value), AssertionTypeDescription.iterable, value), + asyncIterable: (value: unknown): asserts value is AsyncIterable => assertType(is.asyncIterable(value), AssertionTypeDescription.asyncIterable, value), + generator: (value: unknown): asserts value is Generator => assertType(is.generator(value), TypeName.Generator, value), + nativePromise: (value: unknown): asserts value is Promise => assertType(is.nativePromise(value), AssertionTypeDescription.nativePromise, value), + promise: (value: unknown): asserts value is Promise => assertType(is.promise(value), TypeName.Promise, value), + generatorFunction: (value: unknown): asserts value is GeneratorFunction => assertType(is.generatorFunction(value), TypeName.GeneratorFunction, value), + // eslint-disable-next-line @typescript-eslint/ban-types + asyncFunction: (value: unknown): asserts value is Function => assertType(is.asyncFunction(value), TypeName.AsyncFunction, value), + // eslint-disable-next-line @typescript-eslint/ban-types + boundFunction: (value: unknown): asserts value is Function => assertType(is.boundFunction(value), TypeName.Function, value), + regExp: (value: unknown): asserts value is RegExp => assertType(is.regExp(value), TypeName.RegExp, value), + date: (value: unknown): asserts value is Date => assertType(is.date(value), TypeName.Date, value), + error: (value: unknown): asserts value is Error => assertType(is.error(value), TypeName.Error, value), + map: (value: unknown): asserts value is Map => assertType(is.map(value), TypeName.Map, value), + set: (value: unknown): asserts value is Set => assertType(is.set(value), TypeName.Set, value), + weakMap: (value: unknown): asserts value is WeakMap => assertType(is.weakMap(value), TypeName.WeakMap, value), + weakSet: (value: unknown): asserts value is WeakSet => assertType(is.weakSet(value), TypeName.WeakSet, value), + int8Array: (value: unknown): asserts value is Int8Array => assertType(is.int8Array(value), TypeName.Int8Array, value), + uint8Array: (value: unknown): asserts value is Uint8Array => assertType(is.uint8Array(value), TypeName.Uint8Array, value), + uint8ClampedArray: (value: unknown): asserts value is Uint8ClampedArray => assertType(is.uint8ClampedArray(value), TypeName.Uint8ClampedArray, value), + int16Array: (value: unknown): asserts value is Int16Array => assertType(is.int16Array(value), TypeName.Int16Array, value), + uint16Array: (value: unknown): asserts value is Uint16Array => assertType(is.uint16Array(value), TypeName.Uint16Array, value), + int32Array: (value: unknown): asserts value is Int32Array => assertType(is.int32Array(value), TypeName.Int32Array, value), + uint32Array: (value: unknown): asserts value is Uint32Array => assertType(is.uint32Array(value), TypeName.Uint32Array, value), + float32Array: (value: unknown): asserts value is Float32Array => assertType(is.float32Array(value), TypeName.Float32Array, value), + float64Array: (value: unknown): asserts value is Float64Array => assertType(is.float64Array(value), TypeName.Float64Array, value), + bigInt64Array: (value: unknown): asserts value is BigInt64Array => assertType(is.bigInt64Array(value), TypeName.BigInt64Array, value), + bigUint64Array: (value: unknown): asserts value is BigUint64Array => assertType(is.bigUint64Array(value), TypeName.BigUint64Array, value), + arrayBuffer: (value: unknown): asserts value is ArrayBuffer => assertType(is.arrayBuffer(value), TypeName.ArrayBuffer, value), + sharedArrayBuffer: (value: unknown): asserts value is SharedArrayBuffer => assertType(is.sharedArrayBuffer(value), TypeName.SharedArrayBuffer, value), + dataView: (value: unknown): asserts value is DataView => assertType(is.dataView(value), TypeName.DataView, value), + urlInstance: (value: unknown): asserts value is URL => assertType(is.urlInstance(value), TypeName.URL, value), + urlString: (value: unknown): asserts value is string => assertType(is.urlString(value), AssertionTypeDescription.urlString, value), + truthy: (value: unknown): asserts value is unknown => assertType(is.truthy(value), AssertionTypeDescription.truthy, value), + falsy: (value: unknown): asserts value is unknown => assertType(is.falsy(value), AssertionTypeDescription.falsy, value), + nan: (value: unknown): asserts value is unknown => assertType(is.nan(value), AssertionTypeDescription.nan, value), + primitive: (value: unknown): asserts value is Primitive => assertType(is.primitive(value), AssertionTypeDescription.primitive, value), + integer: (value: unknown): asserts value is number => assertType(is.integer(value), AssertionTypeDescription.integer, value), + safeInteger: (value: unknown): asserts value is number => assertType(is.safeInteger(value), AssertionTypeDescription.safeInteger, value), + plainObject: (value: unknown): asserts value is {[key: string]: unknown} => assertType(is.plainObject(value), AssertionTypeDescription.plainObject, value), + typedArray: (value: unknown): asserts value is TypedArray => assertType(is.typedArray(value), AssertionTypeDescription.typedArray, value), + arrayLike: (value: unknown): asserts value is ArrayLike => assertType(is.arrayLike(value), AssertionTypeDescription.arrayLike, value), + domElement: (value: unknown): asserts value is Element => assertType(is.domElement(value), AssertionTypeDescription.domElement, value), + observable: (value: unknown): asserts value is ObservableLike => assertType(is.observable(value), TypeName.Observable, value), + nodeStream: (value: unknown): asserts value is NodeStream => assertType(is.nodeStream(value), AssertionTypeDescription.nodeStream, value), + infinite: (value: unknown): asserts value is number => assertType(is.infinite(value), AssertionTypeDescription.infinite, value), + emptyArray: (value: unknown): asserts value is never[] => assertType(is.emptyArray(value), AssertionTypeDescription.emptyArray, value), + nonEmptyArray: (value: unknown): asserts value is unknown[] => assertType(is.nonEmptyArray(value), AssertionTypeDescription.nonEmptyArray, value), + emptyString: (value: unknown): asserts value is '' => assertType(is.emptyString(value), AssertionTypeDescription.emptyString, value), + nonEmptyString: (value: unknown): asserts value is string => assertType(is.nonEmptyString(value), AssertionTypeDescription.nonEmptyString, value), + emptyStringOrWhitespace: (value: unknown): asserts value is string => assertType(is.emptyStringOrWhitespace(value), AssertionTypeDescription.emptyStringOrWhitespace, value), + emptyObject: (value: unknown): asserts value is {[key: string]: never} => assertType(is.emptyObject(value), AssertionTypeDescription.emptyObject, value), + nonEmptyObject: (value: unknown): asserts value is {[key: string]: unknown} => assertType(is.nonEmptyObject(value), AssertionTypeDescription.nonEmptyObject, value), + emptySet: (value: unknown): asserts value is Set => assertType(is.emptySet(value), AssertionTypeDescription.emptySet, value), + nonEmptySet: (value: unknown): asserts value is Set => assertType(is.nonEmptySet(value), AssertionTypeDescription.nonEmptySet, value), + emptyMap: (value: unknown): asserts value is Map => assertType(is.emptyMap(value), AssertionTypeDescription.emptyMap, value), + nonEmptyMap: (value: unknown): asserts value is Map => assertType(is.nonEmptyMap(value), AssertionTypeDescription.nonEmptyMap, value), + + // Numbers. + evenInteger: (value: number): asserts value is number => assertType(is.evenInteger(value), AssertionTypeDescription.evenInteger, value), + oddInteger: (value: number): asserts value is number => assertType(is.oddInteger(value), AssertionTypeDescription.oddInteger, value), + + // Two arguments. + directInstanceOf: (instance: unknown, class_: Class): asserts instance is T => assertType(is.directInstanceOf(instance, class_), AssertionTypeDescription.directInstanceOf, instance), + inRange: (value: number, range: number | number[]): asserts value is number => assertType(is.inRange(value, range), AssertionTypeDescription.inRange, value), + + // Variadic functions. + any: (predicate: Predicate, ...values: unknown[]): void | never => assertType(is.any(predicate, ...values), AssertionTypeDescription.any, values), + all: (predicate: Predicate, ...values: unknown[]): void | never => assertType(is.all(predicate, ...values), AssertionTypeDescription.all, values) +}; + // Some few keywords are reserved, but we'll populate them for Node.js users // See https://github.com/Microsoft/TypeScript/issues/2536 Object.defineProperties(is, { @@ -398,9 +623,21 @@ Object.defineProperties(is, { value: is.null_ } }); +Object.defineProperties(assert, { + class: { + value: assert.class_ + }, + function: { + value: assert.function_ + }, + null: { + value: assert.null_ + } +}); export default is; // For CommonJS default export support module.exports = is; module.exports.default = is; +module.exports.assert = assert; diff --git a/test/test.ts b/test/test.ts index b7d017d..6e6c4be 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,13 +1,14 @@ -import fs = require('fs'); -import net = require('net'); -import Stream = require('stream'); import {inspect} from 'util'; -import tempy = require('tempy'); import test, {ExecutionContext} from 'ava'; import {JSDOM} from 'jsdom'; import {Subject, Observable} from 'rxjs'; +import is, {assert, AssertionTypeDescription, TypeName} from '../source'; + +import fs = require('fs'); +import net = require('net'); +import Stream = require('stream'); +import tempy = require('tempy'); import ZenObservable = require('zen-observable'); -import is, {TypeName} from '../source'; class PromiseSubclassFixture extends Promise {} class ErrorSubclassFixture extends Error {} @@ -17,14 +18,33 @@ const {document} = window; const createDomElement = (element: string) => document.createElement(element); interface Test { + assert: (...args: any[]) => void | never; fixtures: unknown[]; typename?: TypeName; + typeDescription?: AssertionTypeDescription | TypeName; is(value: unknown): boolean; } +const invertAssertThrow = (description: string, fn: () => void | never, value: unknown): void | never => { + const expectedAssertErrorMessage = `Expected value which is "${description}", received value of type ${is(value)}.`; + + try { + fn(); + } catch (error) { + if (error instanceof TypeError && error.message.includes(expectedAssertErrorMessage)) { + return; + } + + throw error; + } + + throw new Error(`Function did not throw any error, expected: ${expectedAssertErrorMessage}`); +}; + const types = new Map([ ['undefined', { is: is.undefined, + assert: assert.undefined, fixtures: [ undefined ], @@ -32,6 +52,7 @@ const types = new Map([ }], ['null', { is: is.null_, + assert: assert.null_, fixtures: [ null ], @@ -39,6 +60,7 @@ const types = new Map([ }], ['string', { is: is.string, + assert: assert.string, fixtures: [ '🦄', 'hello world', @@ -48,14 +70,17 @@ const types = new Map([ }], ['emptyString', { is: is.emptyString, + assert: assert.emptyString, fixtures: [ '', String() ], - typename: TypeName.string + typename: TypeName.string, + typeDescription: AssertionTypeDescription.emptyString }], ['number', { is: is.number, + assert: assert.number, fixtures: [ 6, 1.4, @@ -68,6 +93,7 @@ const types = new Map([ }], ['bigint', { is: is.bigint, + assert: assert.bigint, fixtures: [ // Disabled until TS supports it for an ESnnnn target. // 1n, @@ -79,6 +105,7 @@ const types = new Map([ }], ['boolean', { is: is.boolean, + assert: assert.boolean, fixtures: [ true, false ], @@ -86,6 +113,7 @@ const types = new Map([ }], ['symbol', { is: is.symbol, + assert: assert.symbol, fixtures: [ Symbol('🦄') ], @@ -93,15 +121,18 @@ const types = new Map([ }], ['numericString', { is: is.numericString, + assert: assert.numericString, fixtures: [ '5', '-3.2', 'Infinity' ], - typename: TypeName.string + typename: TypeName.string, + typeDescription: AssertionTypeDescription.numericString }], ['array', { is: is.array, + assert: assert.array, fixtures: [ [1, 2], new Array(2) @@ -110,14 +141,17 @@ const types = new Map([ }], ['emptyArray', { is: is.emptyArray, + assert: assert.emptyArray, fixtures: [ [], new Array() // eslint-disable-line @typescript-eslint/no-array-constructor ], - typename: TypeName.Array + typename: TypeName.Array, + typeDescription: AssertionTypeDescription.emptyArray }], ['function', { is: is.function_, + assert: assert.function_, fixtures: [ function foo() {}, // eslint-disable-line func-names function () {}, @@ -129,6 +163,7 @@ const types = new Map([ }], ['buffer', { is: is.buffer, + assert: assert.buffer, fixtures: [ Buffer.from('🦄') ], @@ -136,6 +171,7 @@ const types = new Map([ }], ['object', { is: is.object, + assert: assert.object, fixtures: [ {x: 1}, Object.create({x: 1}) @@ -144,6 +180,7 @@ const types = new Map([ }], ['regExp', { is: is.regExp, + assert: assert.regExp, fixtures: [ /\w/, new RegExp('\\w') // eslint-disable-line prefer-regex-literals @@ -152,6 +189,7 @@ const types = new Map([ }], ['date', { is: is.date, + assert: assert.date, fixtures: [ new Date() ], @@ -159,6 +197,7 @@ const types = new Map([ }], ['error', { is: is.error, + assert: assert.error, fixtures: [ new Error('🦄'), new ErrorSubclassFixture() @@ -167,21 +206,26 @@ const types = new Map([ }], ['nativePromise', { is: is.nativePromise, + assert: assert.nativePromise, fixtures: [ Promise.resolve(), PromiseSubclassFixture.resolve() ], - typename: TypeName.Promise + typename: TypeName.Promise, + typeDescription: AssertionTypeDescription.nativePromise }], ['promise', { is: is.promise, + assert: assert.promise, fixtures: [ {then() {}, catch() {}} ], - typename: TypeName.Object + typename: TypeName.Object, + typeDescription: TypeName.Promise }], ['generator', { is: is.generator, + assert: assert.generator, fixtures: [ (function * () { yield 4; @@ -191,23 +235,28 @@ const types = new Map([ }], ['generatorFunction', { is: is.generatorFunction, + assert: assert.generatorFunction, fixtures: [ function * () { yield 4; } ], - typename: TypeName.Function + typename: TypeName.Function, + typeDescription: TypeName.GeneratorFunction }], ['asyncFunction', { is: is.asyncFunction, + assert: assert.asyncFunction, fixtures: [ async function () {}, async () => {} ], - typename: TypeName.Function + typename: TypeName.Function, + typeDescription: TypeName.AsyncFunction }], ['boundFunction', { is: is.boundFunction, + assert: assert.boundFunction, fixtures: [ () => {}, function () {}.bind(null) // eslint-disable-line no-extra-bind @@ -216,6 +265,7 @@ const types = new Map([ }], ['map', { is: is.map, + assert: assert.map, fixtures: [ new Map([['one', '1']]) ], @@ -223,13 +273,16 @@ const types = new Map([ }], ['emptyMap', { is: is.emptyMap, + assert: assert.emptyMap, fixtures: [ new Map() ], - typename: TypeName.Map + typename: TypeName.Map, + typeDescription: AssertionTypeDescription.emptyMap }], ['set', { is: is.set, + assert: assert.set, fixtures: [ new Set(['one']) ], @@ -237,13 +290,16 @@ const types = new Map([ }], ['emptySet', { is: is.emptySet, + assert: assert.emptySet, fixtures: [ new Set() ], - typename: TypeName.Set + typename: TypeName.Set, + typeDescription: AssertionTypeDescription.emptySet }], ['weakSet', { is: is.weakSet, + assert: assert.weakSet, fixtures: [ new WeakSet() ], @@ -251,6 +307,7 @@ const types = new Map([ }], ['weakMap', { is: is.weakMap, + assert: assert.weakMap, fixtures: [ new WeakMap() ], @@ -258,6 +315,7 @@ const types = new Map([ }], ['int8Array', { is: is.int8Array, + assert: assert.int8Array, fixtures: [ new Int8Array() ], @@ -265,6 +323,7 @@ const types = new Map([ }], ['uint8Array', { is: is.uint8Array, + assert: assert.uint8Array, fixtures: [ new Uint8Array() ], @@ -272,6 +331,7 @@ const types = new Map([ }], ['uint8ClampedArray', { is: is.uint8ClampedArray, + assert: assert.uint8ClampedArray, fixtures: [ new Uint8ClampedArray() ], @@ -279,6 +339,7 @@ const types = new Map([ }], ['int16Array', { is: is.int16Array, + assert: assert.int16Array, fixtures: [ new Int16Array() ], @@ -286,6 +347,7 @@ const types = new Map([ }], ['uint16Array', { is: is.uint16Array, + assert: assert.uint16Array, fixtures: [ new Uint16Array() ], @@ -293,6 +355,7 @@ const types = new Map([ }], ['int32Array', { is: is.int32Array, + assert: assert.int32Array, fixtures: [ new Int32Array() ], @@ -300,6 +363,7 @@ const types = new Map([ }], ['uint32Array', { is: is.uint32Array, + assert: assert.uint32Array, fixtures: [ new Uint32Array() ], @@ -307,6 +371,7 @@ const types = new Map([ }], ['float32Array', { is: is.float32Array, + assert: assert.float32Array, fixtures: [ new Float32Array() ], @@ -314,6 +379,7 @@ const types = new Map([ }], ['float64Array', { is: is.float64Array, + assert: assert.float64Array, fixtures: [ new Float64Array() ], @@ -321,6 +387,7 @@ const types = new Map([ }], ['bigInt64Array', { is: is.bigInt64Array, + assert: assert.bigInt64Array, fixtures: [ new BigInt64Array() ], @@ -328,6 +395,7 @@ const types = new Map([ }], ['bigUint64Array', { is: is.bigUint64Array, + assert: assert.bigUint64Array, fixtures: [ new BigUint64Array() ], @@ -335,6 +403,7 @@ const types = new Map([ }], ['arrayBuffer', { is: is.arrayBuffer, + assert: assert.arrayBuffer, fixtures: [ new ArrayBuffer(10) ], @@ -342,6 +411,7 @@ const types = new Map([ }], ['dataView', { is: is.dataView, + assert: assert.dataView, fixtures: [ new DataView(new ArrayBuffer(10)) ], @@ -349,45 +419,56 @@ const types = new Map([ }], ['nan', { is: is.nan, + assert: assert.nan, fixtures: [ NaN, Number.NaN ], - typename: TypeName.number + typename: TypeName.number, + typeDescription: AssertionTypeDescription.nan }], ['nullOrUndefined', { is: is.nullOrUndefined, + assert: assert.nullOrUndefined, fixtures: [ null, undefined - ] + ], + typeDescription: AssertionTypeDescription.nullOrUndefined }], ['plainObject', { is: is.plainObject, + assert: assert.plainObject, fixtures: [ {x: 1}, Object.create(null), new Object() // eslint-disable-line no-new-object ], - typename: TypeName.Object + typename: TypeName.Object, + typeDescription: AssertionTypeDescription.plainObject }], ['integer', { is: is.integer, + assert: assert.integer, fixtures: [ 6 ], - typename: TypeName.number + typename: TypeName.number, + typeDescription: AssertionTypeDescription.integer }], ['safeInteger', { is: is.safeInteger, + assert: assert.safeInteger, fixtures: [ (2 ** 53) - 1, -(2 ** 53) + 1 ], - typename: TypeName.number + typename: TypeName.number, + typeDescription: AssertionTypeDescription.safeInteger }], ['domElement', { is: is.domElement, + assert: assert.domElement, fixtures: [ 'div', 'input', @@ -395,10 +476,12 @@ const types = new Map([ 'img', 'canvas', 'script' - ].map(createDomElement) + ].map(createDomElement), + typeDescription: AssertionTypeDescription.domElement }], ['non-domElements', { is: value => !is.domElement(value), + assert: (value: unknown) => invertAssertThrow(AssertionTypeDescription.domElement, () => assert.domElement(value), value), fixtures: [ document.createTextNode('data'), document.createProcessingInstruction('xml-stylesheet', 'href="mycss.css" type="text/css"'), @@ -410,6 +493,7 @@ const types = new Map([ }], ['observable', { is: is.observable, + assert: assert.observable, fixtures: [ new Observable(), new Subject(), @@ -419,6 +503,7 @@ const types = new Map([ }], ['nodeStream', { is: is.nodeStream, + assert: assert.nodeStream, fixtures: [ fs.createReadStream('readme.md'), fs.createWriteStream(tempy.file()), @@ -430,15 +515,18 @@ const types = new Map([ new Stream.Stream(), new Stream.Writable() ], - typename: TypeName.Object + typename: TypeName.Object, + typeDescription: AssertionTypeDescription.nodeStream }], ['infinite', { is: is.infinite, + assert: assert.infinite, fixtures: [ Infinity, -Infinity ], - typename: TypeName.number + typename: TypeName.number, + typeDescription: AssertionTypeDescription.infinite }] ]); @@ -452,7 +540,7 @@ const testType = (t: ExecutionContext, type: string, exclude?: string[]) => { return; } - const {is: testIs, typename} = testData; + const {is: testIs, assert: testAssert, typename, typeDescription} = testData; for (const [key, {fixtures}] of types) { // TODO: Automatically exclude value types in other tests that we have in the current one. @@ -462,10 +550,13 @@ const testType = (t: ExecutionContext, type: string, exclude?: string[]) => { } const isTypeUnderTest = key === type; - const assert = isTypeUnderTest ? t.true.bind(t) : t.false.bind(t); + const assertIs = isTypeUnderTest ? t.true.bind(t) : t.false.bind(t); + const assertAsserts = isTypeUnderTest ? t.notThrows.bind(t) : t.throws.bind(t); for (const fixture of fixtures) { - assert(testIs(fixture), `Value: ${inspect(fixture)}`); + assertIs(testIs(fixture), `Value: ${inspect(fixture)}`); + const valueType = JSON.stringify(typeDescription ? typeDescription : typename); + assertAsserts(() => testAssert(fixture), `Expected value which is ${valueType}, received value of type ${is(fixture)}.`); if (isTypeUnderTest && typename) { t.is(is(fixture), typename); @@ -506,6 +597,8 @@ test('is.numericString', t => { testType(t, 'numericString'); t.false(is.numericString('')); t.false(is.numericString(1)); + t.throws(() => assert.numericString('')); + t.throws(() => assert.numericString(1)); }); test('is.array', t => { @@ -518,6 +611,7 @@ test('is.function', t => { test('is.boundFunction', t => { t.false(is.boundFunction(function () {})); // eslint-disable-line prefer-arrow-callback + t.throws(() => assert.boundFunction(function () {})); // eslint-disable-line prefer-arrow-callback }); test('is.buffer', t => { @@ -535,6 +629,7 @@ test('is.object', t => { for (const el of testData.fixtures) { t.true(is.object(el)); + t.notThrows(() => assert.object(el)); } }); @@ -565,6 +660,8 @@ test('is.asyncFunction', t => { if (is.asyncFunction(fixture)) { // eslint-disable-next-line promise/prefer-await-to-then t.true(is.function_(fixture().then)); + // eslint-disable-next-line promise/prefer-await-to-then + t.notThrows(() => assert.function_(fixture().then)); } }); @@ -650,9 +747,13 @@ test('is.directInstanceOf', t => { t.true(is.directInstanceOf(error, Error)); t.true(is.directInstanceOf(errorSubclass, ErrorSubclassFixture)); + t.notThrows(() => assert.directInstanceOf(error, Error)); + t.notThrows(() => assert.directInstanceOf(errorSubclass, ErrorSubclassFixture)); t.false(is.directInstanceOf(error, ErrorSubclassFixture)); t.false(is.directInstanceOf(errorSubclass, Error)); + t.throws(() => assert.directInstanceOf(error, ErrorSubclassFixture)); + t.throws(() => assert.directInstanceOf(errorSubclass, Error)); }); test('is.urlInstance', t => { @@ -661,6 +762,11 @@ test('is.urlInstance', t => { t.false(is.urlInstance({})); t.false(is.urlInstance(undefined)); t.false(is.urlInstance(null)); + + t.notThrows(() => assert.urlInstance(url)); + t.throws(() => assert.urlInstance({})); + t.throws(() => assert.urlInstance(undefined)); + t.throws(() => assert.urlInstance(null)); }); test('is.urlString', t => { @@ -670,6 +776,12 @@ test('is.urlString', t => { t.false(is.urlString({})); t.false(is.urlString(undefined)); t.false(is.urlString(null)); + + t.notThrows(() => assert.urlString(url)); + t.throws(() => assert.urlString(new URL(url))); + t.throws(() => assert.urlString({})); + t.throws(() => assert.urlString(undefined)); + t.throws(() => assert.urlString(null)); }); test('is.truthy', t => { @@ -682,6 +794,17 @@ test('is.truthy', t => { // Disabled until TS supports it for an ESnnnn target. // t.true(is.truthy(1n)); t.true(is.truthy(BigInt(1))); + + t.notThrows(() => assert.truthy('unicorn')); + t.notThrows(() => assert.truthy('🦄')); + t.notThrows(() => assert.truthy(new Set())); + t.notThrows(() => assert.truthy(Symbol('🦄'))); + t.notThrows(() => assert.truthy(true)); + t.notThrows(() => assert.truthy(1)); + + // Disabled until TS supports it for an ESnnnn target. + // t.notThrows(() => assert.truthy(1n)); + t.notThrows(() => assert.truthy(BigInt(1))); }); test('is.falsy', t => { @@ -694,6 +817,16 @@ test('is.falsy', t => { // Disabled until TS supports it for an ESnnnn target. // t.true(is.falsy(0n)); t.true(is.falsy(BigInt(0))); + + t.notThrows(() => assert.falsy(false)); + t.notThrows(() => assert.falsy(0)); + t.notThrows(() => assert.falsy('')); + t.notThrows(() => assert.falsy(null)); + t.notThrows(() => assert.falsy(undefined)); + t.notThrows(() => assert.falsy(NaN)); + // Disabled until TS supports it for an ESnnnn target. + // t.notThrows(() => assert.falsy(0n)); + t.notThrows(() => assert.falsy(BigInt(0))); }); test('is.nan', t => { @@ -721,18 +854,22 @@ test('is.primitive', t => { for (const el of primitives) { t.true(is.primitive(el)); + t.notThrows(() => assert.primitive(el)); } }); test('is.integer', t => { testType(t, 'integer', ['number', 'safeInteger']); t.false(is.integer(1.4)); + t.throws(() => assert.integer(1.4)); }); test('is.safeInteger', t => { testType(t, 'safeInteger', ['number', 'integer']); t.false(is.safeInteger(2 ** 53)); t.false(is.safeInteger(-(2 ** 53))); + t.throws(() => assert.safeInteger(2 ** 53)); + t.throws(() => assert.safeInteger(-(2 ** 53))); }); test('is.plainObject', t => { @@ -749,6 +886,16 @@ test('is.iterable', t => { t.false(is.iterable(NaN)); t.false(is.iterable(Infinity)); t.false(is.iterable({})); + + t.notThrows(() => assert.iterable('')); + t.notThrows(() => assert.iterable([])); + t.notThrows(() => assert.iterable(new Map())); + t.throws(() => assert.iterable(null)); + t.throws(() => assert.iterable(undefined)); + t.throws(() => assert.iterable(0)); + t.throws(() => assert.iterable(NaN)); + t.throws(() => assert.iterable(Infinity)); + t.throws(() => assert.iterable({})); }); test('is.asyncIterable', t => { @@ -762,6 +909,17 @@ test('is.asyncIterable', t => { t.false(is.asyncIterable(NaN)); t.false(is.asyncIterable(Infinity)); t.false(is.asyncIterable({})); + + t.notThrows(() => assert.asyncIterable({ + [Symbol.asyncIterator]: () => { } + })); + + t.throws(() => assert.asyncIterable(null)); + t.throws(() => assert.asyncIterable(undefined)); + t.throws(() => assert.asyncIterable(0)); + t.throws(() => assert.asyncIterable(NaN)); + t.throws(() => assert.asyncIterable(Infinity)); + t.throws(() => assert.asyncIterable({})); }); test('is.class', t => { @@ -774,6 +932,7 @@ test('is.class', t => { for (const classDeclaration of classDeclarations) { t.true(is.class_(classDeclaration)); + t.notThrows(() => assert.class_(classDeclaration)); } }); @@ -793,11 +952,16 @@ test('is.typedArray', t => { for (const item of typedArrays) { t.true(is.typedArray(item)); + t.notThrows(() => assert.typedArray(item)); } t.false(is.typedArray(new ArrayBuffer(1))); t.false(is.typedArray([])); t.false(is.typedArray({})); + + t.throws(() => assert.typedArray(new ArrayBuffer(1))); + t.throws(() => assert.typedArray([])); + t.throws(() => assert.typedArray({})); }); test('is.arrayLike', t => { @@ -811,6 +975,17 @@ test('is.arrayLike', t => { t.false(is.arrayLike({})); t.false(is.arrayLike(() => {})); t.false(is.arrayLike(new Map())); + + (function () { + t.notThrows(() => assert.arrayLike(arguments)); // eslint-disable-line prefer-rest-params + })(); + + t.notThrows(() => assert.arrayLike([])); + t.notThrows(() => assert.arrayLike('unicorn')); + + t.throws(() => assert.arrayLike({})); + t.throws(() => assert.arrayLike(() => { })); + t.throws(() => assert.arrayLike(new Map())); }); test('is.inRange', t => { @@ -842,11 +1017,39 @@ test('is.inRange', t => { t.throws(() => { is.inRange(0, [1, 2, 3]); }); + + t.notThrows(() => assert.inRange(x, [0, 5])); + t.notThrows(() => assert.inRange(x, [5, 0])); + t.notThrows(() => assert.inRange(x, [-5, 5])); + t.notThrows(() => assert.inRange(x, [5, -5])); + t.throws(() => assert.inRange(x, [4, 8])); + t.notThrows(() => assert.inRange(-7, [-5, -10])); + t.notThrows(() => assert.inRange(-5, [-5, -10])); + t.notThrows(() => assert.inRange(-10, [-5, -10])); + + t.notThrows(() => assert.inRange(x, 10)); + t.notThrows(() => assert.inRange(0, 0)); + t.notThrows(() => assert.inRange(-2, -3)); + t.throws(() => assert.inRange(x, 2)); + t.throws(() => assert.inRange(-3, -2)); + + t.throws(() => { + assert.inRange(0, []); + }); + + t.throws(() => { + assert.inRange(0, [5]); + }); + + t.throws(() => { + assert.inRange(0, [1, 2, 3]); + }); }); test('is.domElement', t => { testType(t, 'domElement'); t.false(is.domElement({nodeType: 1, nodeName: 'div'})); + t.throws(() => assert.domElement({nodeType: 1, nodeName: 'div'})); const htmlTagNameToTypeName = { div: 'HTMLDivElement', @@ -878,20 +1081,24 @@ test('is.infinite', t => { test('is.evenInteger', t => { for (const el of [-6, 2, 4]) { t.true(is.evenInteger(el)); + t.notThrows(() => assert.evenInteger(el)); } for (const el of [-3, 1, 5]) { t.false(is.evenInteger(el)); + t.throws(() => assert.evenInteger(el)); } }); test('is.oddInteger', t => { for (const el of [-5, 7, 13]) { t.true(is.oddInteger(el)); + t.notThrows(() => assert.oddInteger(el)); } for (const el of [-8, 8, 10]) { t.false(is.oddInteger(el)); + t.throws(() => assert.oddInteger(el)); } }); @@ -903,17 +1110,26 @@ test('is.nonEmptyArray', t => { t.true(is.nonEmptyArray([1, 2, 3])); t.false(is.nonEmptyArray([])); t.false(is.nonEmptyArray(new Array())); // eslint-disable-line @typescript-eslint/no-array-constructor + + t.notThrows(() => assert.nonEmptyArray([1, 2, 3])); + t.throws(() => assert.nonEmptyArray([])); + t.throws(() => assert.nonEmptyArray(new Array())); // eslint-disable-line @typescript-eslint/no-array-constructor }); test('is.emptyString', t => { testType(t, 'emptyString', ['string']); t.false(is.emptyString('🦄')); + t.throws(() => assert.emptyString('🦄')); }); test('is.nonEmptyString', t => { t.false(is.nonEmptyString('')); t.false(is.nonEmptyString(String())); t.true(is.nonEmptyString('🦄')); + + t.throws(() => assert.nonEmptyString('')); + t.throws(() => assert.nonEmptyString(String())); + t.notThrows(() => assert.nonEmptyString('🦄')); }); test('is.emptyStringOrWhitespace', t => { @@ -921,12 +1137,20 @@ test('is.emptyStringOrWhitespace', t => { t.true(is.emptyStringOrWhitespace(' ')); t.false(is.emptyStringOrWhitespace('🦄')); t.false(is.emptyStringOrWhitespace('unicorn')); + + t.notThrows(() => assert.emptyStringOrWhitespace(' ')); + t.throws(() => assert.emptyStringOrWhitespace('🦄')); + t.throws(() => assert.emptyStringOrWhitespace('unicorn')); }); test('is.emptyObject', t => { t.true(is.emptyObject({})); t.true(is.emptyObject(new Object())); // eslint-disable-line no-new-object t.false(is.emptyObject({unicorn: '🦄'})); + + t.notThrows(() => assert.emptyObject({})); + t.notThrows(() => assert.emptyObject(new Object())); // eslint-disable-line no-new-object + t.throws(() => assert.emptyObject({unicorn: '🦄'})); }); test('is.nonEmptyObject', t => { @@ -936,6 +1160,10 @@ test('is.nonEmptyObject', t => { t.false(is.nonEmptyObject({})); t.false(is.nonEmptyObject(new Object())); // eslint-disable-line no-new-object t.true(is.nonEmptyObject({unicorn: '🦄'})); + + t.throws(() => assert.nonEmptyObject({})); + t.throws(() => assert.nonEmptyObject(new Object())); // eslint-disable-line no-new-object + t.notThrows(() => assert.nonEmptyObject({unicorn: '🦄'})); }); test('is.emptySet', t => { @@ -945,9 +1173,11 @@ test('is.emptySet', t => { test('is.nonEmptySet', t => { const tempSet = new Set(); t.false(is.nonEmptySet(tempSet)); + t.throws(() => assert.nonEmptySet(tempSet)); tempSet.add(1); t.true(is.nonEmptySet(tempSet)); + t.notThrows(() => assert.nonEmptySet(tempSet)); }); test('is.emptyMap', t => { @@ -957,9 +1187,11 @@ test('is.emptyMap', t => { test('is.nonEmptyMap', t => { const tempMap = new Map(); t.false(is.nonEmptyMap(tempMap)); + t.throws(() => assert.nonEmptyMap(tempMap)); tempMap.set('unicorn', '🦄'); t.true(is.nonEmptyMap(tempMap)); + t.notThrows(() => assert.nonEmptyMap(tempMap)); }); test('is.any', t => { @@ -975,6 +1207,19 @@ test('is.any', t => { t.throws(() => { is.any(is.string); }); + + t.notThrows(() => assert.any(is.string, {}, true, '🦄')); + t.notThrows(() => assert.any(is.object, false, {}, 'unicorns')); + t.throws(() => assert.any(is.boolean, '🦄', [], 3)); + t.throws(() => assert.any(is.integer, true, 'lol', {})); + + t.throws(() => { + assert.any(null as any, true); + }); + + t.throws(() => { + assert.any(is.string); + }); }); test('is.all', t => { @@ -990,4 +1235,56 @@ test('is.all', t => { t.throws(() => { is.all(is.string); }); + + t.notThrows(() => assert.all(is.object, {}, new Set(), new Map())); + t.notThrows(() => assert.all(is.boolean, true, false)); + t.throws(() => assert.all(is.string, '🦄', [])); + t.throws(() => assert.all(is.set, new Map(), {})); + + t.throws(() => { + assert.all(null as any, true); + }); + + t.throws(() => { + assert.all(is.string); + }); +}); + +test('assert', t => { + // Contrived test showing that typescript acknowledges the type assertion in assert.number(). + // Real world usage includes asserting user input, but here we use a random number/string generator. + t.plan(2); + + const getNumberOrStringRandomly = (): number | string => { + const rnd = Math.random(); + + if (rnd < 0.5) { + return 'sometimes this function returns text'; + } + + return rnd; + }; + + const canUseOnlyNumber = (badlyTypedArgument: any): number => { + // Narrow the type to number, or throw an error at runtime for non-numbers. + assert.number(badlyTypedArgument); + + // Both the type and runtime value is number. + return 1000 * badlyTypedArgument; + }; + + const badlyTypedVariable: any = getNumberOrStringRandomly(); + + t.true(is.number(badlyTypedVariable) || is.string(badlyTypedVariable)); + + // Using try/catch for test purposes only. + try { + const result = canUseOnlyNumber(badlyTypedVariable); + + // Got lucky, the input was a number yielding a good result. + t.true(is.number(result)); + } catch { + // Assertion was tripped. + t.true(is.string(badlyTypedVariable)); + } }); From 11003b925e78aa7b910fc845707cacb3a80d2fe7 Mon Sep 17 00:00:00 2001 From: Sindre Sorhus Date: Wed, 22 Jan 2020 18:47:01 +0700 Subject: [PATCH 3/3] Meta tweaks --- package.json | 1 + readme.md | 35 ++-- source/index.ts | 2 +- test/test.ts | 494 +++++++++++++++++++++++++++++++++++------------- 4 files changed, 386 insertions(+), 146 deletions(-) diff --git a/package.json b/package.json index 5867b4f..4f99826 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "Type check values", "license": "MIT", "repository": "sindresorhus/is", + "funding": "https://github.com/sindresorhus/is?sponsor=1", "author": { "name": "Sindre Sorhus", "email": "sindresorhus@gmail.com", diff --git a/readme.md b/readme.md index 2d55818..45986b8 100644 --- a/readme.md +++ b/readme.md @@ -6,7 +6,6 @@ For example, `is.string('🦄') //=> true` - ## Highlights - Written in TypeScript @@ -15,14 +14,12 @@ For example, `is.string('🦄') //=> true` - Actively maintained - 2 million weekly downloads - ## Install ``` $ npm install @sindresorhus/is ``` - ## Usage ```js @@ -38,15 +35,23 @@ is.number(6); //=> true ``` -[Assertions](#type-assertions) perform the same type checks, but throw errors if the type does not match. +[Assertions](#type-assertions) perform the same type checks, but throw an error if the type does not match. ```js const {assert} = require('@sindresorhus/is'); -assert.string(foo); -// foo is known to be a string here +assert.string(2); +//=> Error: Expected value which is `string`, received value of type `number`. ``` +And with TypeScript: + +```ts +import {assert} from '@sindresorhus/is'; + +assert.string(foo); +// `foo` is now typed as a `string`. +``` ## API @@ -406,7 +411,6 @@ is.all(is.string, '🦄', [], 'unicorns'); //=> false ``` - ## Type guards When using `is` together with TypeScript, [type guards](http://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types) are being used extensively to infer the correct type inside if-else statements. @@ -437,18 +441,20 @@ padLeft('🦄', '🌈'); ## Type assertions -The type guards are also available as [typescript assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions), which throw an error for unexpected types. It is a convenient one-line version of the often repetitive "if-not-expected-type-throw" pattern. +The type guards are also available as [type assertions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions), which throw an error for unexpected types. It is a convenient one-line version of the often repetitive "if-not-expected-type-throw" pattern. ```ts import {assert} from '@sindresorhus/is'; const handleMovieRatingApiResponse = (response: unknown) => { assert.plainObject(response); - // `response` is typed as a plain `object` with `unknown` properties + // `response` is now typed as a plain `object` with `unknown` properties. + assert.number(response.rating); - // `response.rating` is typed as a `number` + // `response.rating` is now typed as a `number`. + assert.string(response.title); - // `response.title` is typed as a `string` + // `response.title` is now typed as a `string`. return `${response.title} (${response.rating * 10})`; }; @@ -456,8 +462,8 @@ const handleMovieRatingApiResponse = (response: unknown) => { handleMovieRatingApiResponse({rating: 0.87, title: 'The Matrix'}); //=> 'The Matrix (8.7)' -handleMovieRatingApiResponse({status: 'system maintenance'}); -//=> throws error +// This throws an error. +handleMovieRatingApiResponse({rating: '🦄'}); ``` ## FAQ @@ -477,14 +483,12 @@ For the ones I found, pick 3 of these. The most common mistakes I noticed in these modules was using `instanceof` for type checking, forgetting that functions are objects, and omitting `symbol` as a primitive. - ## For enterprise Available as part of the Tidelift Subscription. The maintainers of @sindresorhus/is and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-sindresorhus-is?utm_source=npm-sindresorhus-is&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) - ## Related - [ow](https://github.com/sindresorhus/ow) - Function argument validation for humans @@ -498,7 +502,6 @@ The maintainers of @sindresorhus/is and thousands of other packages are working - [is-blob](https://github.com/sindresorhus/is-blob) - Check if a value is a Blob - File-like object of immutable, raw data - [has-emoji](https://github.com/sindresorhus/has-emoji) - Check whether a string has any emoji - ## Maintainers - [Sindre Sorhus](https://github.com/sindresorhus) diff --git a/source/index.ts b/source/index.ts index 36004d2..a6bc71d 100644 --- a/source/index.ts +++ b/source/index.ts @@ -387,7 +387,7 @@ is.all = (predicate: Predicate, ...values: unknown[]): boolean => predicateOnArr const assertType = (condition: boolean, description: string, value: unknown): asserts condition => { if (!condition) { - throw new TypeError(`Expected value which is "${description}", received value of type ${is(value)}.`); + throw new TypeError(`Expected value which is \`${description}\`, received value of type \`${is(value)}\`.`); } }; diff --git a/test/test.ts b/test/test.ts index 6e6c4be..eb681bc 100644 --- a/test/test.ts +++ b/test/test.ts @@ -1,14 +1,13 @@ +import fs = require('fs'); +import net = require('net'); +import Stream = require('stream'); import {inspect} from 'util'; import test, {ExecutionContext} from 'ava'; import {JSDOM} from 'jsdom'; import {Subject, Observable} from 'rxjs'; -import is, {assert, AssertionTypeDescription, TypeName} from '../source'; - -import fs = require('fs'); -import net = require('net'); -import Stream = require('stream'); import tempy = require('tempy'); import ZenObservable = require('zen-observable'); +import is, {assert, AssertionTypeDescription, TypeName} from '../source'; class PromiseSubclassFixture extends Promise {} class ErrorSubclassFixture extends Error {} @@ -26,7 +25,7 @@ interface Test { } const invertAssertThrow = (description: string, fn: () => void | never, value: unknown): void | never => { - const expectedAssertErrorMessage = `Expected value which is "${description}", received value of type ${is(value)}.`; + const expectedAssertErrorMessage = `Expected value which is \`${description}\`, received value of type \`${is(value)}\`.`; try { fn(); @@ -555,8 +554,8 @@ const testType = (t: ExecutionContext, type: string, exclude?: string[]) => { for (const fixture of fixtures) { assertIs(testIs(fixture), `Value: ${inspect(fixture)}`); - const valueType = JSON.stringify(typeDescription ? typeDescription : typename); - assertAsserts(() => testAssert(fixture), `Expected value which is ${valueType}, received value of type ${is(fixture)}.`); + const valueType = typeDescription ? typeDescription : typename; + assertAsserts(() => testAssert(fixture), `Expected value which is \`${valueType!}\`, received value of type \`${is(fixture)}\`.`); if (isTypeUnderTest && typename) { t.is(is(fixture), typename); @@ -597,8 +596,12 @@ test('is.numericString', t => { testType(t, 'numericString'); t.false(is.numericString('')); t.false(is.numericString(1)); - t.throws(() => assert.numericString('')); - t.throws(() => assert.numericString(1)); + t.throws(() => { + assert.numericString(''); + }); + t.throws(() => { + assert.numericString(1); + }); }); test('is.array', t => { @@ -611,7 +614,10 @@ test('is.function', t => { test('is.boundFunction', t => { t.false(is.boundFunction(function () {})); // eslint-disable-line prefer-arrow-callback - t.throws(() => assert.boundFunction(function () {})); // eslint-disable-line prefer-arrow-callback + + t.throws(() => { + assert.boundFunction(function () {}); // eslint-disable-line prefer-arrow-callback + }); }); test('is.buffer', t => { @@ -660,8 +666,11 @@ test('is.asyncFunction', t => { if (is.asyncFunction(fixture)) { // eslint-disable-next-line promise/prefer-await-to-then t.true(is.function_(fixture().then)); - // eslint-disable-next-line promise/prefer-await-to-then - t.notThrows(() => assert.function_(fixture().then)); + + t.notThrows(() => { + // eslint-disable-next-line promise/prefer-await-to-then + assert.function_(fixture().then); + }); } }); @@ -747,13 +756,21 @@ test('is.directInstanceOf', t => { t.true(is.directInstanceOf(error, Error)); t.true(is.directInstanceOf(errorSubclass, ErrorSubclassFixture)); - t.notThrows(() => assert.directInstanceOf(error, Error)); - t.notThrows(() => assert.directInstanceOf(errorSubclass, ErrorSubclassFixture)); + t.notThrows(() => { + assert.directInstanceOf(error, Error); + }); + t.notThrows(() => { + assert.directInstanceOf(errorSubclass, ErrorSubclassFixture); + }); t.false(is.directInstanceOf(error, ErrorSubclassFixture)); t.false(is.directInstanceOf(errorSubclass, Error)); - t.throws(() => assert.directInstanceOf(error, ErrorSubclassFixture)); - t.throws(() => assert.directInstanceOf(errorSubclass, Error)); + t.throws(() => { + assert.directInstanceOf(error, ErrorSubclassFixture); + }); + t.throws(() => { + assert.directInstanceOf(errorSubclass, Error); + }); }); test('is.urlInstance', t => { @@ -763,10 +780,18 @@ test('is.urlInstance', t => { t.false(is.urlInstance(undefined)); t.false(is.urlInstance(null)); - t.notThrows(() => assert.urlInstance(url)); - t.throws(() => assert.urlInstance({})); - t.throws(() => assert.urlInstance(undefined)); - t.throws(() => assert.urlInstance(null)); + t.notThrows(() => { + assert.urlInstance(url); + }); + t.throws(() => { + assert.urlInstance({}); + }); + t.throws(() => { + assert.urlInstance(undefined); + }); + t.throws(() => { + assert.urlInstance(null); + }); }); test('is.urlString', t => { @@ -777,11 +802,21 @@ test('is.urlString', t => { t.false(is.urlString(undefined)); t.false(is.urlString(null)); - t.notThrows(() => assert.urlString(url)); - t.throws(() => assert.urlString(new URL(url))); - t.throws(() => assert.urlString({})); - t.throws(() => assert.urlString(undefined)); - t.throws(() => assert.urlString(null)); + t.notThrows(() => { + assert.urlString(url); + }); + t.throws(() => { + assert.urlString(new URL(url)); + }); + t.throws(() => { + assert.urlString({}); + }); + t.throws(() => { + assert.urlString(undefined); + }); + t.throws(() => { + assert.urlString(null); + }); }); test('is.truthy', t => { @@ -795,16 +830,30 @@ test('is.truthy', t => { // t.true(is.truthy(1n)); t.true(is.truthy(BigInt(1))); - t.notThrows(() => assert.truthy('unicorn')); - t.notThrows(() => assert.truthy('🦄')); - t.notThrows(() => assert.truthy(new Set())); - t.notThrows(() => assert.truthy(Symbol('🦄'))); - t.notThrows(() => assert.truthy(true)); - t.notThrows(() => assert.truthy(1)); + t.notThrows(() => { + assert.truthy('unicorn'); + }); + t.notThrows(() => { + assert.truthy('🦄'); + }); + t.notThrows(() => { + assert.truthy(new Set()); + }); + t.notThrows(() => { + assert.truthy(Symbol('🦄')); + }); + t.notThrows(() => { + assert.truthy(true); + }); + t.notThrows(() => { + assert.truthy(1); + }); - // Disabled until TS supports it for an ESnnnn target. + // TODO: Disabled until TS supports it for an ESnnnn target. // t.notThrows(() => assert.truthy(1n)); - t.notThrows(() => assert.truthy(BigInt(1))); + t.notThrows(() => { + assert.truthy(BigInt(1)); + }); }); test('is.falsy', t => { @@ -814,19 +863,33 @@ test('is.falsy', t => { t.true(is.falsy(null)); t.true(is.falsy(undefined)); t.true(is.falsy(NaN)); - // Disabled until TS supports it for an ESnnnn target. + // TODO: Disabled until TS supports it for an ESnnnn target. // t.true(is.falsy(0n)); t.true(is.falsy(BigInt(0))); - t.notThrows(() => assert.falsy(false)); - t.notThrows(() => assert.falsy(0)); - t.notThrows(() => assert.falsy('')); - t.notThrows(() => assert.falsy(null)); - t.notThrows(() => assert.falsy(undefined)); - t.notThrows(() => assert.falsy(NaN)); - // Disabled until TS supports it for an ESnnnn target. + t.notThrows(() => { + assert.falsy(false); + }); + t.notThrows(() => { + assert.falsy(0); + }); + t.notThrows(() => { + assert.falsy(''); + }); + t.notThrows(() => { + assert.falsy(null); + }); + t.notThrows(() => { + assert.falsy(undefined); + }); + t.notThrows(() => { + assert.falsy(NaN); + }); + // TODO: Disabled until TS supports it for an ESnnnn target. // t.notThrows(() => assert.falsy(0n)); - t.notThrows(() => assert.falsy(BigInt(0))); + t.notThrows(() => { + assert.falsy(BigInt(0)); + }); }); test('is.nan', t => { @@ -852,24 +915,32 @@ test('is.primitive', t => { // 6n ]; - for (const el of primitives) { - t.true(is.primitive(el)); - t.notThrows(() => assert.primitive(el)); + for (const element of primitives) { + t.true(is.primitive(element)); + t.notThrows(() => { + assert.primitive(element); + }); } }); test('is.integer', t => { testType(t, 'integer', ['number', 'safeInteger']); t.false(is.integer(1.4)); - t.throws(() => assert.integer(1.4)); + t.throws(() => { + assert.integer(1.4); + }); }); test('is.safeInteger', t => { testType(t, 'safeInteger', ['number', 'integer']); t.false(is.safeInteger(2 ** 53)); t.false(is.safeInteger(-(2 ** 53))); - t.throws(() => assert.safeInteger(2 ** 53)); - t.throws(() => assert.safeInteger(-(2 ** 53))); + t.throws(() => { + assert.safeInteger(2 ** 53); + }); + t.throws(() => { + assert.safeInteger(-(2 ** 53)); + }); }); test('is.plainObject', t => { @@ -887,15 +958,33 @@ test('is.iterable', t => { t.false(is.iterable(Infinity)); t.false(is.iterable({})); - t.notThrows(() => assert.iterable('')); - t.notThrows(() => assert.iterable([])); - t.notThrows(() => assert.iterable(new Map())); - t.throws(() => assert.iterable(null)); - t.throws(() => assert.iterable(undefined)); - t.throws(() => assert.iterable(0)); - t.throws(() => assert.iterable(NaN)); - t.throws(() => assert.iterable(Infinity)); - t.throws(() => assert.iterable({})); + t.notThrows(() => { + assert.iterable(''); + }); + t.notThrows(() => { + assert.iterable([]); + }); + t.notThrows(() => { + assert.iterable(new Map()); + }); + t.throws(() => { + assert.iterable(null); + }); + t.throws(() => { + assert.iterable(undefined); + }); + t.throws(() => { + assert.iterable(0); + }); + t.throws(() => { + assert.iterable(NaN); + }); + t.throws(() => { + assert.iterable(Infinity); + }); + t.throws(() => { + assert.iterable({}); + }); }); test('is.asyncIterable', t => { @@ -910,16 +999,30 @@ test('is.asyncIterable', t => { t.false(is.asyncIterable(Infinity)); t.false(is.asyncIterable({})); - t.notThrows(() => assert.asyncIterable({ - [Symbol.asyncIterator]: () => { } - })); + t.notThrows(() => { + assert.asyncIterable({ + [Symbol.asyncIterator]: () => { } + }); + }); - t.throws(() => assert.asyncIterable(null)); - t.throws(() => assert.asyncIterable(undefined)); - t.throws(() => assert.asyncIterable(0)); - t.throws(() => assert.asyncIterable(NaN)); - t.throws(() => assert.asyncIterable(Infinity)); - t.throws(() => assert.asyncIterable({})); + t.throws(() => { + assert.asyncIterable(null); + }); + t.throws(() => { + assert.asyncIterable(undefined); + }); + t.throws(() => { + assert.asyncIterable(0); + }); + t.throws(() => { + assert.asyncIterable(NaN); + }); + t.throws(() => { + assert.asyncIterable(Infinity); + }); + t.throws(() => { + assert.asyncIterable({}); + }); }); test('is.class', t => { @@ -932,7 +1035,10 @@ test('is.class', t => { for (const classDeclaration of classDeclarations) { t.true(is.class_(classDeclaration)); - t.notThrows(() => assert.class_(classDeclaration)); + + t.notThrows(() => { + assert.class_(classDeclaration); + }); } }); @@ -952,16 +1058,25 @@ test('is.typedArray', t => { for (const item of typedArrays) { t.true(is.typedArray(item)); - t.notThrows(() => assert.typedArray(item)); + + t.notThrows(() => { + assert.typedArray(item); + }); } t.false(is.typedArray(new ArrayBuffer(1))); t.false(is.typedArray([])); t.false(is.typedArray({})); - t.throws(() => assert.typedArray(new ArrayBuffer(1))); - t.throws(() => assert.typedArray([])); - t.throws(() => assert.typedArray({})); + t.throws(() => { + assert.typedArray(new ArrayBuffer(1)); + }); + t.throws(() => { + assert.typedArray([]); + }); + t.throws(() => { + assert.typedArray({}); + }); }); test('is.arrayLike', t => { @@ -977,15 +1092,27 @@ test('is.arrayLike', t => { t.false(is.arrayLike(new Map())); (function () { - t.notThrows(() => assert.arrayLike(arguments)); // eslint-disable-line prefer-rest-params + t.notThrows(() => { + assert.arrayLike(arguments); // eslint-disable-line prefer-rest-params + }); })(); - t.notThrows(() => assert.arrayLike([])); - t.notThrows(() => assert.arrayLike('unicorn')); + t.notThrows(() => { + assert.arrayLike([]); + }); + t.notThrows(() => { + assert.arrayLike('unicorn'); + }); - t.throws(() => assert.arrayLike({})); - t.throws(() => assert.arrayLike(() => { })); - t.throws(() => assert.arrayLike(new Map())); + t.throws(() => { + assert.arrayLike({}); + }); + t.throws(() => { + assert.arrayLike(() => {}); + }); + t.throws(() => { + assert.arrayLike(new Map()); + }); }); test('is.inRange', t => { @@ -1018,20 +1145,57 @@ test('is.inRange', t => { is.inRange(0, [1, 2, 3]); }); - t.notThrows(() => assert.inRange(x, [0, 5])); - t.notThrows(() => assert.inRange(x, [5, 0])); - t.notThrows(() => assert.inRange(x, [-5, 5])); - t.notThrows(() => assert.inRange(x, [5, -5])); - t.throws(() => assert.inRange(x, [4, 8])); - t.notThrows(() => assert.inRange(-7, [-5, -10])); - t.notThrows(() => assert.inRange(-5, [-5, -10])); - t.notThrows(() => assert.inRange(-10, [-5, -10])); + t.notThrows(() => { + assert.inRange(x, [0, 5]); + }); - t.notThrows(() => assert.inRange(x, 10)); - t.notThrows(() => assert.inRange(0, 0)); - t.notThrows(() => assert.inRange(-2, -3)); - t.throws(() => assert.inRange(x, 2)); - t.throws(() => assert.inRange(-3, -2)); + t.notThrows(() => { + assert.inRange(x, [5, 0]); + }); + + t.notThrows(() => { + assert.inRange(x, [-5, 5]); + }); + + t.notThrows(() => { + assert.inRange(x, [5, -5]); + }); + + t.throws(() => { + assert.inRange(x, [4, 8]); + }); + + t.notThrows(() => { + assert.inRange(-7, [-5, -10]); + }); + + t.notThrows(() => { + assert.inRange(-5, [-5, -10]); + }); + + t.notThrows(() => { + assert.inRange(-10, [-5, -10]); + }); + + t.notThrows(() => { + assert.inRange(x, 10); + }); + + t.notThrows(() => { + assert.inRange(0, 0); + }); + + t.notThrows(() => { + assert.inRange(-2, -3); + }); + + t.throws(() => { + assert.inRange(x, 2); + }); + + t.throws(() => { + assert.inRange(-3, -2); + }); t.throws(() => { assert.inRange(0, []); @@ -1049,7 +1213,9 @@ test('is.inRange', t => { test('is.domElement', t => { testType(t, 'domElement'); t.false(is.domElement({nodeType: 1, nodeName: 'div'})); - t.throws(() => assert.domElement({nodeType: 1, nodeName: 'div'})); + t.throws(() => { + assert.domElement({nodeType: 1, nodeName: 'div'}); + }); const htmlTagNameToTypeName = { div: 'HTMLDivElement', @@ -1081,24 +1247,32 @@ test('is.infinite', t => { test('is.evenInteger', t => { for (const el of [-6, 2, 4]) { t.true(is.evenInteger(el)); - t.notThrows(() => assert.evenInteger(el)); + t.notThrows(() => { + assert.evenInteger(el); + }); } for (const el of [-3, 1, 5]) { t.false(is.evenInteger(el)); - t.throws(() => assert.evenInteger(el)); + t.throws(() => { + assert.evenInteger(el); + }); } }); test('is.oddInteger', t => { for (const el of [-5, 7, 13]) { t.true(is.oddInteger(el)); - t.notThrows(() => assert.oddInteger(el)); + t.notThrows(() => { + assert.oddInteger(el); + }); } for (const el of [-8, 8, 10]) { t.false(is.oddInteger(el)); - t.throws(() => assert.oddInteger(el)); + t.throws(() => { + assert.oddInteger(el); + }); } }); @@ -1111,15 +1285,23 @@ test('is.nonEmptyArray', t => { t.false(is.nonEmptyArray([])); t.false(is.nonEmptyArray(new Array())); // eslint-disable-line @typescript-eslint/no-array-constructor - t.notThrows(() => assert.nonEmptyArray([1, 2, 3])); - t.throws(() => assert.nonEmptyArray([])); - t.throws(() => assert.nonEmptyArray(new Array())); // eslint-disable-line @typescript-eslint/no-array-constructor + t.notThrows(() => { + assert.nonEmptyArray([1, 2, 3]); + }); + t.throws(() => { + assert.nonEmptyArray([]); + }); + t.throws(() => { + assert.nonEmptyArray(new Array()); // eslint-disable-line @typescript-eslint/no-array-constructor + }); }); test('is.emptyString', t => { testType(t, 'emptyString', ['string']); t.false(is.emptyString('🦄')); - t.throws(() => assert.emptyString('🦄')); + t.throws(() => { + assert.emptyString('🦄'); + }); }); test('is.nonEmptyString', t => { @@ -1127,9 +1309,15 @@ test('is.nonEmptyString', t => { t.false(is.nonEmptyString(String())); t.true(is.nonEmptyString('🦄')); - t.throws(() => assert.nonEmptyString('')); - t.throws(() => assert.nonEmptyString(String())); - t.notThrows(() => assert.nonEmptyString('🦄')); + t.throws(() => { + assert.nonEmptyString(''); + }); + t.throws(() => { + assert.nonEmptyString(String()); + }); + t.notThrows(() => { + assert.nonEmptyString('🦄'); + }); }); test('is.emptyStringOrWhitespace', t => { @@ -1138,9 +1326,15 @@ test('is.emptyStringOrWhitespace', t => { t.false(is.emptyStringOrWhitespace('🦄')); t.false(is.emptyStringOrWhitespace('unicorn')); - t.notThrows(() => assert.emptyStringOrWhitespace(' ')); - t.throws(() => assert.emptyStringOrWhitespace('🦄')); - t.throws(() => assert.emptyStringOrWhitespace('unicorn')); + t.notThrows(() => { + assert.emptyStringOrWhitespace(' '); + }); + t.throws(() => { + assert.emptyStringOrWhitespace('🦄'); + }); + t.throws(() => { + assert.emptyStringOrWhitespace('unicorn'); + }); }); test('is.emptyObject', t => { @@ -1148,9 +1342,15 @@ test('is.emptyObject', t => { t.true(is.emptyObject(new Object())); // eslint-disable-line no-new-object t.false(is.emptyObject({unicorn: '🦄'})); - t.notThrows(() => assert.emptyObject({})); - t.notThrows(() => assert.emptyObject(new Object())); // eslint-disable-line no-new-object - t.throws(() => assert.emptyObject({unicorn: '🦄'})); + t.notThrows(() => { + assert.emptyObject({}); + }); + t.notThrows(() => { + assert.emptyObject(new Object()); // eslint-disable-line no-new-object + }); + t.throws(() => { + assert.emptyObject({unicorn: '🦄'}); + }); }); test('is.nonEmptyObject', t => { @@ -1161,9 +1361,15 @@ test('is.nonEmptyObject', t => { t.false(is.nonEmptyObject(new Object())); // eslint-disable-line no-new-object t.true(is.nonEmptyObject({unicorn: '🦄'})); - t.throws(() => assert.nonEmptyObject({})); - t.throws(() => assert.nonEmptyObject(new Object())); // eslint-disable-line no-new-object - t.notThrows(() => assert.nonEmptyObject({unicorn: '🦄'})); + t.throws(() => { + assert.nonEmptyObject({}); + }); + t.throws(() => { + assert.nonEmptyObject(new Object()); // eslint-disable-line no-new-object + }); + t.notThrows(() => { + assert.nonEmptyObject({unicorn: '🦄'}); + }); }); test('is.emptySet', t => { @@ -1173,11 +1379,15 @@ test('is.emptySet', t => { test('is.nonEmptySet', t => { const tempSet = new Set(); t.false(is.nonEmptySet(tempSet)); - t.throws(() => assert.nonEmptySet(tempSet)); + t.throws(() => { + assert.nonEmptySet(tempSet); + }); tempSet.add(1); t.true(is.nonEmptySet(tempSet)); - t.notThrows(() => assert.nonEmptySet(tempSet)); + t.notThrows(() => { + assert.nonEmptySet(tempSet); + }); }); test('is.emptyMap', t => { @@ -1187,11 +1397,15 @@ test('is.emptyMap', t => { test('is.nonEmptyMap', t => { const tempMap = new Map(); t.false(is.nonEmptyMap(tempMap)); - t.throws(() => assert.nonEmptyMap(tempMap)); + t.throws(() => { + assert.nonEmptyMap(tempMap); + }); tempMap.set('unicorn', '🦄'); t.true(is.nonEmptyMap(tempMap)); - t.notThrows(() => assert.nonEmptyMap(tempMap)); + t.notThrows(() => { + assert.nonEmptyMap(tempMap); + }); }); test('is.any', t => { @@ -1208,10 +1422,21 @@ test('is.any', t => { is.any(is.string); }); - t.notThrows(() => assert.any(is.string, {}, true, '🦄')); - t.notThrows(() => assert.any(is.object, false, {}, 'unicorns')); - t.throws(() => assert.any(is.boolean, '🦄', [], 3)); - t.throws(() => assert.any(is.integer, true, 'lol', {})); + t.notThrows(() => { + assert.any(is.string, {}, true, '🦄'); + }); + + t.notThrows(() => { + assert.any(is.object, false, {}, 'unicorns'); + }); + + t.throws(() => { + assert.any(is.boolean, '🦄', [], 3); + }); + + t.throws(() => { + assert.any(is.integer, true, 'lol', {}); + }); t.throws(() => { assert.any(null as any, true); @@ -1236,10 +1461,21 @@ test('is.all', t => { is.all(is.string); }); - t.notThrows(() => assert.all(is.object, {}, new Set(), new Map())); - t.notThrows(() => assert.all(is.boolean, true, false)); - t.throws(() => assert.all(is.string, '🦄', [])); - t.throws(() => assert.all(is.set, new Map(), {})); + t.notThrows(() => { + assert.all(is.object, {}, new Set(), new Map()); + }); + + t.notThrows(() => { + assert.all(is.boolean, true, false); + }); + + t.throws(() => { + assert.all(is.string, '🦄', []); + }); + + t.throws(() => { + assert.all(is.set, new Map(), {}); + }); t.throws(() => { assert.all(null as any, true); @@ -1251,18 +1487,18 @@ test('is.all', t => { }); test('assert', t => { - // Contrived test showing that typescript acknowledges the type assertion in assert.number(). - // Real world usage includes asserting user input, but here we use a random number/string generator. + // Contrived test showing that TypeScript acknowledges the type assertion in `assert.number()`. + // Real--world usage includes asserting user input, but here we use a random number/string generator. t.plan(2); const getNumberOrStringRandomly = (): number | string => { - const rnd = Math.random(); + const random = Math.random(); - if (rnd < 0.5) { + if (random < 0.5) { return 'sometimes this function returns text'; } - return rnd; + return random; }; const canUseOnlyNumber = (badlyTypedArgument: any): number => {