Skip to content

Commit

Permalink
[patch] fix isArray checks for readonly arrays
Browse files Browse the repository at this point in the history
electrovir committed Sep 14, 2024
1 parent 3ecf780 commit 51b36e8
Showing 11 changed files with 188 additions and 115 deletions.
163 changes: 83 additions & 80 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augment-vir/mono-repo-root",
"version": "30.0.3",
"version": "30.0.4",
"private": true,
"homepage": "https://github.com/electrovir/augment-vir",
"bugs": {
@@ -48,7 +48,7 @@
"esbuild": "^0.23.1",
"eslint": "^9.10.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-jsdoc": "^50.2.2",
"eslint-plugin-jsdoc": "^50.2.3",
"eslint-plugin-monorepo-cop": "^1.0.2",
"eslint-plugin-playwright": "^1.6.2",
"eslint-plugin-prettier": "^5.2.1",
4 changes: 2 additions & 2 deletions packages/assert/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augment-vir/assert",
"version": "30.0.3",
"version": "30.0.4",
"description": "A collection of assertions for test and production code alike.",
"keywords": [
"augment",
@@ -41,7 +41,7 @@
"test:update": "npm test"
},
"dependencies": {
"@augment-vir/core": "^30.0.2",
"@augment-vir/core": "^30.0.4",
"@date-vir/duration": "^6.0.0",
"deep-eql": "^5.0.2",
"expect-type": "^0.20.0",
56 changes: 55 additions & 1 deletion packages/assert/src/assertions/runtime-type.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import {AnyFunction, AnyObject, UnknownObject} from '@augment-vir/core';
import {
AnyFunction,
AnyObject,
JsonCompatibleArray,
JsonCompatibleObject,
JsonCompatibleValue,
UnknownObject,
} from '@augment-vir/core';
import {describe, it} from '@augment-vir/test';
import {assertWrap} from '../augments/guards/assert-wrap.js';
import {assert} from '../augments/guards/assert.js';
@@ -36,6 +43,11 @@ describe('isArray', () => {

assert.tsType(actualPassUnion).equals<ExpectedUnionNarrowedType>();
});
it('narrows readonly', () => {
const value: string | ReadonlyArray<string> = [] as any;
assert.isArray(value);
assert.tsType(value).equals<ReadonlyArray<string>>();
});
});
describe('check', () => {
it('guards', () => {
@@ -51,6 +63,26 @@ describe('isArray', () => {
it('rejects', () => {
assert.isFalse(check.isArray(actualReject));
});
it('narrows', () => {
if (check.isArray(actualPassUnion)) {
assert.tsType(actualPassUnion).equals<ExpectedUnionNarrowedType>();
}
});
it('narrows readonly', () => {
const value: string | ReadonlyArray<string> = [] as any;

if (check.isArray(value)) {
assert.tsType(value).equals<ReadonlyArray<string>>();
}
});
it('narrows to writable first', () => {
const value: JsonCompatibleObject | JsonCompatibleArray = [] as any;

if (check.isArray(value)) {
assert.tsType(value).equals<JsonCompatibleValue[]>();
value.push('something');
}
});
});
describe('assertWrap', () => {
it('guards', () => {
@@ -63,6 +95,12 @@ describe('isArray', () => {
it('rejects', () => {
assert.throws(() => assertWrap.isArray(actualReject));
});
it('narrows readonly', () => {
const value: string | ReadonlyArray<string> = [] as any;
const checked = assertWrap.isArray(value);
assert.tsType(checked).equals<ReadonlyArray<string>>();
assert.deepEquals(checked, []);
});
});
describe('checkWrap', () => {
it('guards', () => {
@@ -76,6 +114,13 @@ describe('isArray', () => {
it('rejects', () => {
assert.isUndefined(checkWrap.isArray(actualReject));
});
it('narrows readonly', () => {
const value: string | ReadonlyArray<string> = [] as any;
const checked = checkWrap.isArray(value);
assert.isDefined(checked);
assert.tsType(checked).equals<ReadonlyArray<string>>();
assert.deepEquals(checked, []);
});
});
describe('waitUntil', () => {
it('guards', async () => {
@@ -96,6 +141,15 @@ describe('isArray', () => {
waitUntil.isArray(() => actualReject, waitUntilTestOptions, 'failure'),
);
});
it('narrows readonly', async () => {
const value: string | ReadonlyArray<string> = [] as any;

const newValue = await waitUntil.isArray(() => value, waitUntilTestOptions, 'failure');

assert.tsType(newValue).equals<ReadonlyArray<string>>();

assert.deepEquals(value, newValue);
});
});
});
describe('isNotArray', () => {
30 changes: 23 additions & 7 deletions packages/assert/src/assertions/runtime-type.ts
Original file line number Diff line number Diff line change
@@ -11,6 +11,15 @@ import type {GuardGroup} from '../guard-types/guard-group.js';
import {autoGuard, autoGuardSymbol} from '../guard-types/guard-override.js';
import {WaitUntilOptions} from '../guard-types/wait-until-function.js';

type ArrayNarrow<Actual> =
Extract<Actual, unknown[]> extends never
? Extract<Actual, ReadonlyArray<unknown>> extends never
? unknown[] extends Actual
? unknown[]
: never
: Extract<Actual, ReadonlyArray<unknown>>
: Extract<Actual, unknown[]>;

function isNotArray<const Actual>(
actual: Actual,
failureMessage?: string | undefined,
@@ -72,10 +81,10 @@ function isNotNull<const Actual>(
assertNotRuntimeType(actual, 'null', failureMessage);
}

function isArray(
actual: unknown,
function isArray<const Actual>(
actual: Actual,
failureMessage?: string | undefined,
): asserts actual is unknown[] {
): asserts actual is ArrayNarrow<Actual> {
assertRuntimeType(actual, 'array', failureMessage);
}
function isBigInt(actual: unknown, failureMessage?: string | undefined): asserts actual is bigint {
@@ -547,7 +556,7 @@ export const runtimeTypeGuards = {
* @see
* - {@link check.isNotArray} : the opposite check.
*/
isArray: autoGuardSymbol,
isArray: autoGuard<<Actual>(actual: Actual) => actual is ArrayNarrow<Actual>>(),
/**
* Checks that a value is a BigInt.
*
@@ -978,7 +987,7 @@ export const runtimeTypeGuards = {
* @see
* - {@link assertWrap.isNotArray} : the opposite assertion.
*/
isArray: autoGuardSymbol,
isArray: autoGuard<<Actual>(actual: Actual) => ArrayNarrow<Actual>>(),
/**
* Asserts that a value is a BigInt. Returns the value if the assertion passes.
*
@@ -1467,7 +1476,7 @@ export const runtimeTypeGuards = {
* @see
* - {@link checkWrap.isNotArray} : the opposite check.
*/
isArray: autoGuardSymbol,
isArray: autoGuard<<Actual>(actual: Actual) => ArrayNarrow<Actual> | undefined>(),
/**
* Checks that a value is a BigInt. Returns the value if the check passes, otherwise
* `undefined`.
@@ -1919,7 +1928,14 @@ export const runtimeTypeGuards = {
* @see
* - {@link waitUntil.isNotArray} : the opposite assertion.
*/
isArray: autoGuardSymbol,
isArray:
autoGuard<
<const Actual>(
callback: () => MaybePromise<Actual>,
options?: WaitUntilOptions | undefined,
failureMessage?: string | undefined,
) => Promise<ArrayNarrow<Actual>>
>(),
/**
* Repeatedly calls a callback until its output is a BigInt. Once the callback output
* passes, it is returned. If the attempts time out, an error is thrown.
6 changes: 3 additions & 3 deletions packages/common/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augment-vir/common",
"version": "30.0.3",
"version": "30.0.4",
"description": "A collection of augments, helpers types, functions, and classes for any JavaScript environment.",
"keywords": [
"augment",
@@ -39,8 +39,8 @@
"test:web": "virmator --no-deps test web"
},
"dependencies": {
"@augment-vir/assert": "^30.0.2",
"@augment-vir/core": "^30.0.2",
"@augment-vir/assert": "^30.0.4",
"@augment-vir/core": "^30.0.4",
"@date-vir/duration": "^6.0.0",
"ansi-styles": "^6.2.1",
"json5": "^2.2.3",
4 changes: 2 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augment-vir/core",
"version": "30.0.3",
"version": "30.0.4",
"description": "Core augment-vir augments. Use @augment-vir/common instead.",
"homepage": "https://github.com/electrovir/augment-vir",
"bugs": {
@@ -33,7 +33,7 @@
"type-fest": "^4.26.1"
},
"devDependencies": {
"@types/node": "^22.5.4",
"@types/node": "^22.5.5",
"c8": "^10.1.2",
"istanbul-smart-text-reporter": "^1.1.4"
},
10 changes: 5 additions & 5 deletions packages/node/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augment-vir/node",
"version": "30.0.3",
"version": "30.0.4",
"description": "A collection of augments, helpers types, functions, and classes only for Node.js (backend) JavaScript environments.",
"keywords": [
"augment",
@@ -37,18 +37,18 @@
"test:update": "npm test"
},
"dependencies": {
"@augment-vir/assert": "^30.0.2",
"@augment-vir/common": "^30.0.2",
"@augment-vir/assert": "^30.0.4",
"@augment-vir/common": "^30.0.4",
"@date-vir/duration": "^6.0.0",
"ansi-styles": "^6.2.1",
"terminate": "^2.8.0",
"type-fest": "^4.26.1",
"typed-event-target": "^3.4.0"
},
"devDependencies": {
"@augment-vir/test": "^30.0.2",
"@augment-vir/test": "^30.0.4",
"@prisma/client": "^5.19.1",
"@types/node": "^22.5.4",
"@types/node": "^22.5.5",
"@web/dev-server-esbuild": "^1.0.2",
"@web/test-runner": "^0.19.0",
"@web/test-runner-commands": "^0.9.0",
10 changes: 5 additions & 5 deletions packages/scripts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augment-vir/scripts",
"version": "30.0.3",
"version": "30.0.4",
"private": true,
"homepage": "https://github.com/electrovir/augment-vir",
"bugs": {
@@ -24,16 +24,16 @@
"test:update": "npm test"
},
"dependencies": {
"@augment-vir/assert": "^30.0.2",
"@augment-vir/core": "^30.0.2",
"@augment-vir/assert": "^30.0.4",
"@augment-vir/core": "^30.0.4",
"@virmator/docs": "^13.3.12",
"jsdom": "^25.0.0",
"typedoc": "^0.26.7"
},
"devDependencies": {
"@augment-vir/test": "^30.0.2",
"@augment-vir/test": "^30.0.4",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.5.4"
"@types/node": "^22.5.5"
},
"engines": {
"node": ">=22"
8 changes: 4 additions & 4 deletions packages/test/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augment-vir/test",
"version": "30.0.3",
"version": "30.0.4",
"description": "A universal testing suite that works with Mocha style test runners _and_ Node.js's built-in test runner.",
"keywords": [
"test",
@@ -42,13 +42,13 @@
"test:web": "virmator test --no-deps web 'src/test-web/**/*.test.ts'"
},
"dependencies": {
"@augment-vir/assert": "^30.0.2",
"@augment-vir/common": "^30.0.2",
"@augment-vir/assert": "^30.0.4",
"@augment-vir/common": "^30.0.4",
"@open-wc/testing-helpers": "^3.0.1",
"type-fest": "^4.26.1"
},
"devDependencies": {
"@types/node": "^22.5.4",
"@types/node": "^22.5.5",
"@web/dev-server-esbuild": "^1.0.2",
"@web/test-runner": "^0.19.0",
"@web/test-runner-commands": "^0.9.0",
8 changes: 4 additions & 4 deletions packages/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@augment-vir/web",
"version": "30.0.3",
"version": "30.0.4",
"description": "A collection of augments, helpers types, functions, and classes only for web (frontend) JavaScript environments.",
"keywords": [
"augment",
@@ -35,14 +35,14 @@
"test:update": "npm test"
},
"dependencies": {
"@augment-vir/assert": "^30.0.2",
"@augment-vir/common": "^30.0.2",
"@augment-vir/assert": "^30.0.4",
"@augment-vir/common": "^30.0.4",
"@date-vir/duration": "^6.0.0",
"html-spec-tags": "^2.2.0",
"type-fest": "^4.26.1"
},
"devDependencies": {
"@augment-vir/test": "^30.0.2",
"@augment-vir/test": "^30.0.4",
"bowser": "^2.11.0",
"typescript": "^5.6.2"
},

0 comments on commit 51b36e8

Please sign in to comment.