Skip to content

Commit a2fd842

Browse files
committed
feat!(assert): remove options from assert
Now, `assert` only throws an `AssertError` with the default message. Use `@core/errorutil/alter` to throw a custom error.
1 parent 3c7c494 commit a2fd842

File tree

3 files changed

+49
-106
lines changed

3 files changed

+49
-106
lines changed

README.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,22 @@ const a: unknown = "Hello";
194194
// `assert` does nothing or throws an `AssertError`
195195
assert(a, is.String);
196196
// a is now narrowed to string
197+
```
198+
199+
Use [`@core/errorutil/alter`](https://jsr.io/@core/errorutil/doc/alter/~/alter)
200+
to throw a custom error:
197201

198-
// With custom message
199-
assert(a, is.String, { message: "a must be a string" });
202+
```typescript
203+
import { alter } from "@core/errorutil/alter";
204+
import { assert, is } from "@core/unknownutil";
205+
206+
const a: unknown = 0;
207+
208+
// The following throws an Error("a is not a string")
209+
alter(
210+
() => assert(a, is.String),
211+
new Error("a is not a string"),
212+
);
200213
```
201214

202215
### ensure

assert.ts

Lines changed: 33 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,81 +1,49 @@
11
import type { Predicate } from "./type.ts";
22

3-
/**
4-
* A factory function that generates assertion error messages.
5-
*/
6-
export type AssertMessageFactory = (
7-
x: unknown,
8-
pred: Predicate<unknown>,
9-
name?: string,
10-
) => string;
11-
12-
/**
13-
* The default factory function used to generate assertion error messages.
14-
*/
15-
export const defaultAssertMessageFactory: AssertMessageFactory = (
16-
x,
17-
pred,
18-
name,
19-
) => {
3+
const assertMessageFactory = (x: unknown, pred: Predicate<unknown>) => {
204
const p = pred.name || "anonymous predicate";
215
const t = typeof x;
226
const v = JSON.stringify(x, null, 2);
23-
return `Expected ${
24-
name ?? "a value"
25-
} that satisfies the predicate ${p}, got ${t}: ${v}`;
7+
return `Expected a value that satisfies the predicate ${p}, got ${t}: ${v}`;
268
};
279

28-
let assertMessageFactory = defaultAssertMessageFactory;
29-
3010
/**
3111
* Represents an error that occurs when an assertion fails.
3212
*/
3313
export class AssertError extends Error {
3414
/**
35-
* Constructs a new instance.
36-
* @param message The error message.
15+
* The value that failed the assertion.
3716
*/
38-
constructor(message?: string) {
39-
super(message);
17+
readonly x: unknown;
4018

41-
if (Error.captureStackTrace) {
42-
Error.captureStackTrace(this, AssertError);
43-
}
19+
/**
20+
* The predicate that the value failed to satisfy.
21+
*/
22+
readonly pred: Predicate<unknown>;
4423

24+
/**
25+
* Constructs a new instance.
26+
*
27+
* @param x The value that failed the assertion.
28+
* @param pred The predicate that the value failed to satisfy.
29+
*/
30+
constructor(x: unknown, pred: Predicate<unknown>) {
31+
super(assertMessageFactory(x, pred));
4532
this.name = this.constructor.name;
33+
this.x = x;
34+
this.pred = pred;
4635
}
4736
}
4837

49-
/**
50-
* Sets the factory function used to generate assertion error messages.
51-
*
52-
* ```ts
53-
* import { is, setAssertMessageFactory } from "@core/unknownutil";
54-
*
55-
* setAssertMessageFactory((x, pred) => {
56-
* if (pred === is.String) {
57-
* return `Expected a string, got ${typeof x}`;
58-
* } else if (pred === is.Number) {
59-
* return `Expected a number, got ${typeof x}`;
60-
* } else if (pred === is.Boolean) {
61-
* return `Expected a boolean, got ${typeof x}`;
62-
* } else {
63-
* return `Expected a value that satisfies the predicate, got ${typeof x}`;
64-
* }
65-
* });
66-
* ```
67-
*
68-
* @param factory The factory function.
69-
*/
70-
export function setAssertMessageFactory(factory: AssertMessageFactory): void {
71-
assertMessageFactory = factory;
72-
}
73-
7438
/**
7539
* Asserts that the given value satisfies the provided predicate.
7640
*
7741
* It throws {@linkcode AssertError} if the value does not satisfy the predicate.
7842
*
43+
* @param x The value to be asserted.
44+
* @param pred The predicate function to test the value against.
45+
* @returns The function has a return type of `asserts x is T` to help TypeScript narrow down the type of `x` after the assertion.
46+
*
7947
* ```ts
8048
* import { assert, is } from "@core/unknownutil";
8149
*
@@ -84,19 +52,22 @@ export function setAssertMessageFactory(factory: AssertMessageFactory): void {
8452
* // a is now narrowed to string
8553
* ```
8654
*
87-
* @param x The value to be asserted.
88-
* @param pred The predicate function to test the value against.
89-
* @param options Optional configuration for the assertion.
90-
* @returns The function has a return type of `asserts x is T` to help TypeScript narrow down the type of `x` after the assertion.
55+
* Use {@linkcode https://jsr.io/@core/errorutil/doc/alter/~/alter|@core/errorutil/alter.alter} to alter error.
56+
*
57+
* ```ts
58+
* import { alter } from "@core/errorutil/alter";
59+
* import { assert, is } from "@core/unknownutil";
60+
*
61+
* const a: unknown = 42;
62+
* alter(() => assert(a, is.String), new Error("a is not a string"));
63+
* // Error: a is not a string
64+
* ```
9165
*/
9266
export function assert<T>(
9367
x: unknown,
9468
pred: Predicate<T>,
95-
options: { message?: string; name?: string } = {},
9669
): asserts x is T {
9770
if (!pred(x)) {
98-
throw new AssertError(
99-
options.message ?? assertMessageFactory(x, pred, options.name),
100-
);
71+
throw new AssertError(x, pred);
10172
}
10273
}

assert_test.ts

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { assertThrows } from "@std/assert";
2-
import {
3-
assert,
4-
AssertError,
5-
defaultAssertMessageFactory,
6-
setAssertMessageFactory,
7-
} from "./assert.ts";
2+
import { assert, AssertError } from "./assert.ts";
83

94
const x: unknown = Symbol("x");
105

@@ -39,40 +34,4 @@ Deno.test("assert", async (t) => {
3934
);
4035
},
4136
);
42-
43-
await t.step(
44-
"throws an `AssertError` on false predicate with a custom name",
45-
() => {
46-
assertThrows(
47-
() => assert(x, falsePredicate, { name: "hello world" }),
48-
AssertError,
49-
`Expected hello world that satisfies the predicate falsePredicate, got symbol: undefined`,
50-
);
51-
},
52-
);
53-
54-
await t.step(
55-
"throws an `AssertError` with a custom message on false predicate",
56-
() => {
57-
assertThrows(
58-
() => assert(x, falsePredicate, { message: "Hello" }),
59-
AssertError,
60-
"Hello",
61-
);
62-
},
63-
);
64-
});
65-
66-
Deno.test("setAssertMessageFactory", async (t) => {
67-
setAssertMessageFactory((x, pred) => `Hello ${typeof x} ${pred.name}`);
68-
69-
await t.step("change `AssertError` message on `assert` failure", () => {
70-
assertThrows(
71-
() => assert(x, falsePredicate),
72-
AssertError,
73-
"Hello symbol falsePredicate",
74-
);
75-
});
76-
77-
setAssertMessageFactory(defaultAssertMessageFactory);
7837
});

0 commit comments

Comments
 (0)