assertie 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +38 -21
- package/lib/index.d.ts +9 -8
- package/lib/index.js +14 -8
- package/package.json +1 -1
- package/src/index.ts +13 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.0](https://github.com/OfficialHalfwayDead/assertie/compare/v0.1.0...v0.2.0) (2025-02-17)
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
* [`a721e02`](https://github.com/OfficialHalfwayDead/assertie/commit/a721e02b2e8b7378f4dc017e02c2dd48ba899bfb) Renamed all `assertTypeof` to `assertTypeOf` for readability and consistency with `assertInstanceOf`. Sorry for the instant breaking change if anyone was already using it. Better now than carrying that inconsistency around forever.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* [`76ff555`](https://github.com/OfficialHalfwayDead/assertie/commit/76ff555cbb3a17d27c07642b078f96cd807812ba) Add `assertNull` as it was the only option in `assertType(val, null)` that didn't have an individual function counterpart.
|
package/README.md
CHANGED
|
@@ -2,15 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
Debug assertions for TypeScript, auto tree-shaken by vite for production.
|
|
4
4
|
|
|
5
|
-
Why? Because asserts are simple to read and safer than casting. Use them when you know something is guaranteed to be true, and assertie will make sure it is in dev builds.
|
|
5
|
+
Why? Because asserts are simple to read and safer than casting. As programmers, we sometimes make wrong assumptions. Use them when you know something is guaranteed to be true, and assertie will make sure it actually is in dev builds.
|
|
6
6
|
|
|
7
7
|
```ts
|
|
8
8
|
import { assertInstanceOf } from "assertie";
|
|
9
9
|
...
|
|
10
|
+
|
|
10
11
|
const original = document.getElementById("probably-mounted");
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
// If the element isn’t found, we exit here.
|
|
14
|
+
// Asserts are not meant for validating uncertain values.
|
|
15
|
+
if (original === null) return;
|
|
16
|
+
|
|
17
|
+
// cloneNode returns type Node, but we know it will always
|
|
18
|
+
// be an HTMLElement, because `original` was one.
|
|
19
|
+
const clone = original.cloneNode(true);
|
|
13
20
|
assertInstanceOf(clone, HTMLElement);
|
|
21
|
+
// Unlike casting, the assert will throw if you were mistaken,
|
|
22
|
+
// or if someone accidentally changed const original = document;
|
|
14
23
|
clone.innerText = "No `as` cast needed! 0 overhead in production.";
|
|
15
24
|
```
|
|
16
25
|
|
|
@@ -53,7 +62,8 @@ const config: UserConfig = {
|
|
|
53
62
|
```ts
|
|
54
63
|
import { assert } from "assertie";
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
const a = "yup";
|
|
66
|
+
assert(a === "yup", "optional text if assertion fails");
|
|
57
67
|
|
|
58
68
|
// You get type narrowing from assertie's assertions
|
|
59
69
|
const x: boolean = true;
|
|
@@ -66,7 +76,8 @@ const y: true = x; // no error
|
|
|
66
76
|
```ts
|
|
67
77
|
import { assertType } from "assertie";
|
|
68
78
|
|
|
69
|
-
// assertType can take
|
|
79
|
+
// assertType can take null, undefined, a class/constructable
|
|
80
|
+
// or a primitive JS type string (e.g., "number")
|
|
70
81
|
assertType(1, "number");
|
|
71
82
|
assertType(() => {}, "function");
|
|
72
83
|
assertType(undefined, "undefined");
|
|
@@ -84,14 +95,15 @@ const good: string = value; // no `as` needed
|
|
|
84
95
|
You don't need to use strings to narrow primitives. There are specific functions for specific types if you prefer that.
|
|
85
96
|
|
|
86
97
|
```ts
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
98
|
+
assertTypeOfString("yup");
|
|
99
|
+
assertTypeOfNumber(123);
|
|
100
|
+
assertTypeOfBoolean(true);
|
|
101
|
+
assertTypeOfBigint(123n);
|
|
102
|
+
assertTypeOfUndefined(undefined);
|
|
103
|
+
assertNull(null); // different because typeof null === "object"
|
|
104
|
+
assertTypeOfFunction(() => {});
|
|
105
|
+
assertTypeOfObject({});
|
|
106
|
+
assertTypeOfSymbol(Symbol("yup"));
|
|
95
107
|
assertInstanceOf(new Date(), Date);
|
|
96
108
|
```
|
|
97
109
|
|
|
@@ -105,7 +117,9 @@ let hoisted: string | null | undefined = null;
|
|
|
105
117
|
const f = () => {
|
|
106
118
|
assertNonNullable(hoisted);
|
|
107
119
|
// vs.
|
|
108
|
-
if (hoisted === null || hoisted === undefined)
|
|
120
|
+
if (hoisted === null || hoisted === undefined) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
109
123
|
|
|
110
124
|
console.log(hoisted.toUpperCase());
|
|
111
125
|
};
|
|
@@ -116,10 +130,10 @@ f();
|
|
|
116
130
|
|
|
117
131
|
There are multiple ways in which an assert is better in this specific case:
|
|
118
132
|
|
|
119
|
-
1. Making the code intention clear.
|
|
120
|
-
2. The assert will throw an error in dev if the case that should never happen does happen. Without
|
|
133
|
+
1. Making the code intention clear. `f` was never meant to only run its body some of the time. It is always supposed to work. But the if statement was required to make the compiler happy.
|
|
134
|
+
2. The assert will throw an error in dev if the case that should never happen does happen. Without the assert, any potential behavior change due to `hoisted` not being set is more likely to go unnoticed.
|
|
121
135
|
3. It's a little shorter, mainly if you have to check null and undefined and maybe have prettier rules for { brackets } on if statements.
|
|
122
|
-
4. The assert will be removed in production, so there's no overhead. If that makes you uncomfortable, you can just
|
|
136
|
+
4. The assert will be removed in production, so there's no overhead. If that makes you uncomfortable, you can just can still put the if statement below the assert and reap the benefits of the first two points.
|
|
123
137
|
|
|
124
138
|
```ts
|
|
125
139
|
// Sometimes not your entire object is null
|
|
@@ -138,11 +152,14 @@ const safeObj = obj;
|
|
|
138
152
|
// typeof safeObj === { a: string, b: string, c: number }
|
|
139
153
|
```
|
|
140
154
|
|
|
141
|
-
The reason an array is needed here is because undefined properties may not be present in `Object.keys`, so the caller needs to provide all keys to check. Don't worry about the safety, though. If you forget to pass a key,
|
|
155
|
+
The reason an array is needed here is because undefined properties may not be present in `Object.keys`, so the caller needs to provide all keys to check. Don't worry about the safety, though. If you forget to pass a key, its type will remain nullable after the assert and TypeScript will not consider it safe to access.
|
|
142
156
|
|
|
143
157
|
### Asserting unreachable code
|
|
144
158
|
|
|
145
|
-
The unreachable assertion
|
|
159
|
+
The unreachable assertion will
|
|
160
|
+
|
|
161
|
+
1. ensure switch/if statements are exhaustive at compile time.
|
|
162
|
+
2. throw an error at runtime if TypeScript types are inaccurate.
|
|
146
163
|
|
|
147
164
|
```ts
|
|
148
165
|
const x: "a" | "b" = "a";
|
|
@@ -165,8 +182,8 @@ For now, there is only one:
|
|
|
165
182
|
|
|
166
183
|
```ts
|
|
167
184
|
assertFiniteNumber(123);
|
|
168
|
-
// Ensures passed value is typeof number and
|
|
185
|
+
// Ensures passed value is typeof number and isFinite(num)
|
|
169
186
|
// DO NOT USE FOR INPUT VALIDATION OF USER PROVIDED VALUES!
|
|
170
187
|
```
|
|
171
188
|
|
|
172
|
-
Useful for string-to-number conversions when you expect valid number strings. Prevents accidental usage of strings or invalid numbers due to
|
|
189
|
+
Useful for string-to-number conversions when you expect **valid** number strings. Prevents accidental usage of strings or invalid numbers due to JS's loose equality (`123 == "123"`).
|
package/lib/index.d.ts
CHANGED
|
@@ -20,14 +20,15 @@ type AllJSTypes = PrimitiveTypeStrings | null | undefined | Constructor<unknown>
|
|
|
20
20
|
type ResolveAnyJSType<T extends AllJSTypes> = T extends PrimitiveTypeStrings ? PrimitiveTypes[T] : T extends null ? null : T extends undefined ? undefined : T extends Constructor<infer U> ? U : never;
|
|
21
21
|
export declare function assert(hasToBeTrue: boolean, msg?: string): asserts hasToBeTrue is true;
|
|
22
22
|
export declare function assertType<T extends AllJSTypes>(obj: unknown, expectedType: T): asserts obj is ResolveAnyJSType<T>;
|
|
23
|
-
export declare function
|
|
24
|
-
export declare function
|
|
25
|
-
export declare function
|
|
26
|
-
export declare function
|
|
27
|
-
export declare function
|
|
28
|
-
export declare function
|
|
29
|
-
export declare function
|
|
30
|
-
export declare function
|
|
23
|
+
export declare function assertTypeOfString(obj: unknown): asserts obj is string;
|
|
24
|
+
export declare function assertTypeOfNumber(obj: unknown): asserts obj is number;
|
|
25
|
+
export declare function assertTypeOfBoolean(obj: unknown): asserts obj is boolean;
|
|
26
|
+
export declare function assertTypeOfBigint(obj: unknown): asserts obj is bigint;
|
|
27
|
+
export declare function assertTypeOfUndefined(obj: unknown): asserts obj is undefined;
|
|
28
|
+
export declare function assertTypeOfFunction(obj: unknown): asserts obj is Function;
|
|
29
|
+
export declare function assertTypeOfObject(obj: unknown): asserts obj is object;
|
|
30
|
+
export declare function assertTypeOfSymbol(obj: unknown): asserts obj is symbol;
|
|
31
|
+
export declare function assertNull(obj: unknown): asserts obj is null;
|
|
31
32
|
export declare function assertInstanceOf<T>(obj: unknown, constructable: Constructor<T>): asserts obj is T;
|
|
32
33
|
export declare function assertUnreachable(obj: never, msg?: string): asserts obj is never;
|
|
33
34
|
export declare function assertPropsNonNullable<T, N extends NullableKeys<T>>(obj: T, props: N[]): asserts obj is PropsNonNullable<T, N>;
|
package/lib/index.js
CHANGED
|
@@ -23,55 +23,61 @@ export function assertType(obj, expectedType) {
|
|
|
23
23
|
return;
|
|
24
24
|
throw new AssertionError(`Provided object was not of type ${(typeof expectedType !== "string") ? expectedType?.name : expectedType ?? expectedType}. Was: ${(obj === null) ? "null" : obj?.constructor?.name ?? typeof obj}, value: ${obj}`);
|
|
25
25
|
}
|
|
26
|
-
export function
|
|
26
|
+
export function assertTypeOfString(obj) {
|
|
27
27
|
if (!import.meta.env.DEV)
|
|
28
28
|
return;
|
|
29
29
|
if (typeof obj !== "string")
|
|
30
30
|
throw new AssertionError(`Provided object was not of type string. Was: ${typeof obj}`);
|
|
31
31
|
}
|
|
32
|
-
export function
|
|
32
|
+
export function assertTypeOfNumber(obj) {
|
|
33
33
|
if (!import.meta.env.DEV)
|
|
34
34
|
return;
|
|
35
35
|
if (typeof obj !== "number")
|
|
36
36
|
throw new AssertionError(`Provided object was not of type number. Was: ${typeof obj}`);
|
|
37
37
|
}
|
|
38
|
-
export function
|
|
38
|
+
export function assertTypeOfBoolean(obj) {
|
|
39
39
|
if (!import.meta.env.DEV)
|
|
40
40
|
return;
|
|
41
41
|
if (typeof obj !== "boolean")
|
|
42
42
|
throw new AssertionError(`Provided object was not of type boolean. Was: ${typeof obj}`);
|
|
43
43
|
}
|
|
44
|
-
export function
|
|
44
|
+
export function assertTypeOfBigint(obj) {
|
|
45
45
|
if (!import.meta.env.DEV)
|
|
46
46
|
return;
|
|
47
47
|
if (typeof obj !== "bigint")
|
|
48
48
|
throw new AssertionError(`Provided object was not of type bigint. Was: ${typeof obj}`);
|
|
49
49
|
}
|
|
50
|
-
export function
|
|
50
|
+
export function assertTypeOfUndefined(obj) {
|
|
51
51
|
if (!import.meta.env.DEV)
|
|
52
52
|
return;
|
|
53
53
|
if (typeof obj !== "undefined")
|
|
54
54
|
throw new AssertionError(`Provided object was not of type undefined. Was: ${typeof obj}`);
|
|
55
55
|
}
|
|
56
56
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
57
|
-
export function
|
|
57
|
+
export function assertTypeOfFunction(obj) {
|
|
58
58
|
if (!import.meta.env.DEV)
|
|
59
59
|
return;
|
|
60
60
|
if (typeof obj !== "function")
|
|
61
61
|
throw new AssertionError(`Provided object was not of type function. Was: ${typeof obj}`);
|
|
62
62
|
}
|
|
63
|
-
export function
|
|
63
|
+
export function assertTypeOfObject(obj) {
|
|
64
64
|
if (!import.meta.env.DEV)
|
|
65
65
|
return;
|
|
66
66
|
if (typeof obj !== "object")
|
|
67
67
|
throw new AssertionError(`Provided object was not of type object. Was: ${typeof obj}`);
|
|
68
68
|
}
|
|
69
|
-
export function
|
|
69
|
+
export function assertTypeOfSymbol(obj) {
|
|
70
70
|
if (!import.meta.env.DEV)
|
|
71
71
|
return;
|
|
72
72
|
if (typeof obj !== "symbol")
|
|
73
73
|
throw new AssertionError(`Provided object was not of type symbol. Was: ${typeof obj}`);
|
|
74
74
|
}
|
|
75
|
+
export function assertNull(obj) {
|
|
76
|
+
if (!import.meta.env.DEV)
|
|
77
|
+
return;
|
|
78
|
+
if (obj !== null)
|
|
79
|
+
throw new AssertionError(`Provided object was not null. Was: ${obj}`);
|
|
80
|
+
}
|
|
75
81
|
export function assertInstanceOf(obj, constructable) {
|
|
76
82
|
if (!import.meta.env.DEV)
|
|
77
83
|
return;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -50,47 +50,52 @@ export function assertType<T extends AllJSTypes>(obj: unknown, expectedType: T):
|
|
|
50
50
|
throw new AssertionError(`Provided object was not of type ${(typeof expectedType !== "string") ? expectedType?.name : expectedType ?? expectedType}. Was: ${(obj === null) ? "null" : obj?.constructor?.name ?? typeof obj}, value: ${obj}`);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
export function
|
|
53
|
+
export function assertTypeOfString(obj: unknown): asserts obj is string {
|
|
54
54
|
if (!import.meta.env.DEV) return;
|
|
55
55
|
if (typeof obj !== "string") throw new AssertionError(`Provided object was not of type string. Was: ${typeof obj}`);
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
export function
|
|
58
|
+
export function assertTypeOfNumber(obj: unknown): asserts obj is number {
|
|
59
59
|
if (!import.meta.env.DEV) return;
|
|
60
60
|
if (typeof obj !== "number") throw new AssertionError(`Provided object was not of type number. Was: ${typeof obj}`);
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export function
|
|
63
|
+
export function assertTypeOfBoolean(obj: unknown): asserts obj is boolean {
|
|
64
64
|
if (!import.meta.env.DEV) return;
|
|
65
65
|
if (typeof obj !== "boolean") throw new AssertionError(`Provided object was not of type boolean. Was: ${typeof obj}`);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export function
|
|
68
|
+
export function assertTypeOfBigint(obj: unknown): asserts obj is bigint {
|
|
69
69
|
if (!import.meta.env.DEV) return;
|
|
70
70
|
if (typeof obj !== "bigint") throw new AssertionError(`Provided object was not of type bigint. Was: ${typeof obj}`);
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
export function
|
|
73
|
+
export function assertTypeOfUndefined(obj: unknown): asserts obj is undefined {
|
|
74
74
|
if (!import.meta.env.DEV) return;
|
|
75
75
|
if (typeof obj !== "undefined") throw new AssertionError(`Provided object was not of type undefined. Was: ${typeof obj}`);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
79
|
-
export function
|
|
79
|
+
export function assertTypeOfFunction(obj: unknown): asserts obj is Function {
|
|
80
80
|
if (!import.meta.env.DEV) return;
|
|
81
81
|
if (typeof obj !== "function") throw new AssertionError(`Provided object was not of type function. Was: ${typeof obj}`);
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
export function
|
|
84
|
+
export function assertTypeOfObject(obj: unknown): asserts obj is object {
|
|
85
85
|
if (!import.meta.env.DEV) return;
|
|
86
86
|
if (typeof obj !== "object") throw new AssertionError(`Provided object was not of type object. Was: ${typeof obj}`);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
export function
|
|
89
|
+
export function assertTypeOfSymbol(obj: unknown): asserts obj is symbol {
|
|
90
90
|
if (!import.meta.env.DEV) return;
|
|
91
91
|
if (typeof obj !== "symbol") throw new AssertionError(`Provided object was not of type symbol. Was: ${typeof obj}`);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
export function assertNull(obj: unknown): asserts obj is null {
|
|
95
|
+
if (!import.meta.env.DEV) return;
|
|
96
|
+
if (obj !== null) throw new AssertionError(`Provided object was not null. Was: ${obj}`);
|
|
97
|
+
}
|
|
98
|
+
|
|
94
99
|
export function assertInstanceOf<T>(obj: unknown, constructable: Constructor<T>): asserts obj is T {
|
|
95
100
|
if (!import.meta.env.DEV) return;
|
|
96
101
|
if (!(obj instanceof constructable)) throw new AssertionError(`Provided object was not of type ${constructable.name} but was type: ${(obj === null) ? "null" : obj?.constructor?.name ?? typeof obj}, value: ${obj}`);
|