assertie 0.3.0 → 0.3.2
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 +12 -0
- package/README.md +83 -17
- package/lib/index.d.ts +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/manual_test/index.ts +0 -35
- package/migration-guide-template.md +0 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2](https://github.com/OfficialHalfwayDead/assertie/compare/v0.3.1...v0.3.2) (2025-02-27)
|
|
4
|
+
|
|
5
|
+
* [`0814dd2`](https://github.com/OfficialHalfwayDead/assertie/commit/0814dd2a6b9daf2bbeb99e2925710cc8a76caf07) Bugfix: Constructor type wasn't properly typed and prevent `assertInstanceOf` and `assertType` from accepting many construcable types.
|
|
6
|
+
* [`940e4e4`](https://github.com/OfficialHalfwayDead/assertie/commit/940e4e424c14db20734ed4e2ec2b8a6eeae3aef3) Clarify SSR vite settings and Svelte/Proxy pitfalls in the README.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## [0.3.1](https://github.com/OfficialHalfwayDead/assertie/compare/v0.3.0...v0.3.1) (2025-02-23)
|
|
10
|
+
|
|
11
|
+
* [`0d59837`](https://github.com/OfficialHalfwayDead/assertie/commit/0d59837841707c963ce174e79a527fcb1379ac4f) Add array and tuple assertions to the README.
|
|
12
|
+
|
|
13
|
+
|
|
3
14
|
## [0.3.0](https://github.com/OfficialHalfwayDead/assertie/compare/v0.2.0...v0.3.0) (2025-02-23)
|
|
4
15
|
|
|
5
16
|
### Breaking Changes
|
|
@@ -17,6 +28,7 @@
|
|
|
17
28
|
|
|
18
29
|
* [`075fecd`](https://github.com/OfficialHalfwayDead/assertie/commit/075fecd105030191eac671d8731765f4f5af2cd4) Add JSDoc comments to all functions and improve documentation overall.
|
|
19
30
|
|
|
31
|
+
|
|
20
32
|
## [0.2.0](https://github.com/OfficialHalfwayDead/assertie/compare/v0.1.0...v0.2.0) (2025-02-17)
|
|
21
33
|
|
|
22
34
|
### Breaking Changes
|
package/README.md
CHANGED
|
@@ -29,6 +29,23 @@ clone.innerText = "No `as` cast needed! 0 overhead in production.";
|
|
|
29
29
|
npm i -D assertie
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
By default, vite will tree-shake the functions in the frontend, but for SSR it will actually not touch libraries unless you specify that the module should not be externalized.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
// vite.config.ts
|
|
36
|
+
const config: UserConfig = {
|
|
37
|
+
build: {
|
|
38
|
+
ssr: { // crucial if you have SSR
|
|
39
|
+
noExternal: ["assertie"],
|
|
40
|
+
},
|
|
41
|
+
rollupOptions: {
|
|
42
|
+
treeshake: true, // this is default
|
|
43
|
+
// just make sure it's not explicitly false
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
32
49
|
If you're getting errors when using the package, it's likely because your TypeScript targets are too low:
|
|
33
50
|
|
|
34
51
|
```json
|
|
@@ -42,18 +59,6 @@ If you're getting errors when using the package, it's likely because your TypeSc
|
|
|
42
59
|
}
|
|
43
60
|
```
|
|
44
61
|
|
|
45
|
-
By default, vite will tree-shake the functions for production, but make sure you haven't specifically disabled it in your vite config:
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
// vite.config.ts
|
|
49
|
-
const config: UserConfig = {
|
|
50
|
-
build: {
|
|
51
|
-
rollupOptions: {
|
|
52
|
-
treeshake: true,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
```
|
|
57
62
|
|
|
58
63
|
## Usage
|
|
59
64
|
|
|
@@ -76,7 +81,7 @@ const y: true = x; // no error
|
|
|
76
81
|
```ts
|
|
77
82
|
import { assertType } from "assertie";
|
|
78
83
|
|
|
79
|
-
// assertType can take null, undefined, a class/constructable
|
|
84
|
+
// assertType can take null, undefined, a class/constructable,
|
|
80
85
|
// or a primitive JS type string (e.g., "number")
|
|
81
86
|
assertType(1, "number");
|
|
82
87
|
assertType(() => {}, "function");
|
|
@@ -109,7 +114,7 @@ assertInstanceOf(new Date(), Date);
|
|
|
109
114
|
|
|
110
115
|
### Asserting non-null
|
|
111
116
|
|
|
112
|
-
Sometimes you'll find yourself in a situation where you know from the calling context that a hoisted variable is not `null` or `undefined`, but TypeScript doesn't.
|
|
117
|
+
Sometimes, you'll find yourself in a situation where you know from the calling context that a hoisted variable is not `null` or `undefined`, but TypeScript doesn't.
|
|
113
118
|
|
|
114
119
|
```ts
|
|
115
120
|
let hoisted: string | null | undefined = null;
|
|
@@ -128,9 +133,9 @@ hoisted = "yup";
|
|
|
128
133
|
f();
|
|
129
134
|
```
|
|
130
135
|
|
|
131
|
-
There are multiple
|
|
136
|
+
There are multiple advantages to using an assertion in this case:
|
|
132
137
|
|
|
133
|
-
|
|
138
|
+
It clarifies the code's intent. f was never meant to conditionally execute its body. It is always supposed to work, but the if statement was required to satisfy the compiler.
|
|
134
139
|
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.
|
|
135
140
|
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.
|
|
136
141
|
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.
|
|
@@ -154,12 +159,34 @@ const safeObj = obj;
|
|
|
154
159
|
|
|
155
160
|
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.
|
|
156
161
|
|
|
162
|
+
### Arrays and tuples
|
|
163
|
+
|
|
164
|
+
Arrays and tuples have equivalent versions of `assertType` and `assertNonNullable`. These make it easy to check every element at once and provide excellent error messages and type narrowing.
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
const arr: (number | string | null)[] = [1, 2, 3];
|
|
168
|
+
assertArrayNonNullable(arr); // narrows to (number | string)[]
|
|
169
|
+
assertArrayType(arr, "number"); // narrows to number[]
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
const arr: number[] = [1, 2, 3];
|
|
174
|
+
assertIsTuple(arr, 3); // narrows to [number, number, number]
|
|
175
|
+
|
|
176
|
+
const arrMixed: (number | string | null)[] = [1, "a"];
|
|
177
|
+
assertIsTuple(arrMixed, 2); // narrows to [T, T]
|
|
178
|
+
// where T = number | string | null;
|
|
179
|
+
assertTupleNonNullable(arrMixed); // narrows T to number | string
|
|
180
|
+
assertTupleTypes(arrMixed, ["number", "string"]);
|
|
181
|
+
const tup: [number, string] = arrMixed;
|
|
182
|
+
```
|
|
183
|
+
|
|
157
184
|
### Asserting unreachable code
|
|
158
185
|
|
|
159
186
|
The unreachable assertion will
|
|
160
187
|
|
|
161
188
|
1. ensure switch/if statements are exhaustive at compile time.
|
|
162
|
-
2. throw an error at runtime if TypeScript types are inaccurate.
|
|
189
|
+
2. throw an error at runtime if some TypeScript types are inaccurate.
|
|
163
190
|
|
|
164
191
|
```ts
|
|
165
192
|
const x: "a" | "b" = "a";
|
|
@@ -195,3 +222,42 @@ const num = Number(numStr); // NaN
|
|
|
195
222
|
assertFiniteNumber(num); // throws
|
|
196
223
|
arr[num] = "yup" // disaster averted
|
|
197
224
|
```
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
## Pitfalls
|
|
228
|
+
|
|
229
|
+
While assertions will never throw in production, complete removal of the code may not happen if you call another function inside the assertion call:
|
|
230
|
+
|
|
231
|
+
```js
|
|
232
|
+
assert(object.foo() === "yup");
|
|
233
|
+
|
|
234
|
+
// bundler leaves this stub:
|
|
235
|
+
function assert(hasToBeTrue, msg = "No specific message provided.") {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Since `foo()` might have side effects, it is not possible to remove the entire line. And it seems, the vite bundler **is not capable** of turning the code into:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
// stays because of potential side effects
|
|
244
|
+
const assertValue = object.foo() === "yup";
|
|
245
|
+
assert(assertValue); // gets removed now
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Therefore, you'll have to do that yourself, or if you know `foo()` is a pure function, you can mark it as such:
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
assert(/* @__PURE__ */ object.foo() === "yup");
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Svelte
|
|
255
|
+
|
|
256
|
+
Accessing the value of a rune `x` compiles to `get(x)`, leading to the same pitfall as above. To prevent this, you need to treat the rune like a function:
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
let rune = $state(1);
|
|
260
|
+
let otherRune = $state(1);
|
|
261
|
+
|
|
262
|
+
assert(/*@__PURE__*/ rune === /*@__PURE__*/ otherRune);
|
|
263
|
+
```
|
package/lib/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ declare type NullableKeys<T> = {
|
|
|
16
16
|
declare type PropsNonNullable<T, N extends NullableKeys<T>> = T & {
|
|
17
17
|
[K in N]-?: NonNullable<T[K]>;
|
|
18
18
|
};
|
|
19
|
-
declare type Constructor<T> = new (...args:
|
|
19
|
+
declare type Constructor<T> = new (...args: any[]) => T;
|
|
20
20
|
declare type AllJSTypes = PrimitiveTypeStrings | null | undefined | Constructor<unknown>;
|
|
21
21
|
declare 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;
|
|
22
22
|
/**
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -22,7 +22,7 @@ type PropsNonNullable<T, N extends NullableKeys<T>> = T & {
|
|
|
22
22
|
[K in N]-?: NonNullable<T[K]>;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
type Constructor<T> = new (...args:
|
|
25
|
+
type Constructor<T> = new (...args: any[]) => T;
|
|
26
26
|
|
|
27
27
|
type AllJSTypes = PrimitiveTypeStrings | null | undefined | Constructor<unknown>;
|
|
28
28
|
|
package/manual_test/index.ts
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { assertArrayNonNullable, assertIsTuple, assertTupleNonNullable, assertTupleTypes, assertType } from "../src/index";
|
|
2
|
-
|
|
3
|
-
const func = (tuple: [unknown, unknown, unknown]) => {
|
|
4
|
-
console.log(tuple);
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
const tuple: [unknown, unknown, unknown] = [1, 2, 3];
|
|
8
|
-
const tuple3 = [1, 2, 3] as const;
|
|
9
|
-
const tuple1: [number | undefined, number] = [1, 2];
|
|
10
|
-
const arr: (number|null)[] = [1, 2, 3];
|
|
11
|
-
assertIsTuple(arr, 2);
|
|
12
|
-
assertTupleNonNullable(arr);
|
|
13
|
-
const nntuple1: [number, number] = arr;
|
|
14
|
-
tuple1[0] = 1;
|
|
15
|
-
// assertTupleTypes(tuple3, ["number", "number", "number"]);
|
|
16
|
-
|
|
17
|
-
type Tuple<T, N extends number, A extends unknown[] = []> = A extends { length: N } ? A : Tuple<T, N, [...A, T]>;
|
|
18
|
-
|
|
19
|
-
type Tuple20 = Tuple<number, 20>;
|
|
20
|
-
|
|
21
|
-
func(tuple);
|
|
22
|
-
|
|
23
|
-
const tuple2 = tuple3;
|
|
24
|
-
// const tuple5: [number, number, number | undefined] = tuple3;
|
|
25
|
-
|
|
26
|
-
type Tuple3 = typeof tuple3;
|
|
27
|
-
type Tuple3Length = Tuple3["length"];
|
|
28
|
-
|
|
29
|
-
function func2<T extends number[]>(
|
|
30
|
-
nums: T & (number extends T["length"] ? unknown : never)
|
|
31
|
-
) {
|
|
32
|
-
console.log(nums);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// func2(tuple3);
|