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 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 ways in which an assert is better in this specific case:
136
+ There are multiple advantages to using an assertion in this case:
132
137
 
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.
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: unknown[]) => T;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assertie",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Debug assertions for TypeScript, auto tree-shaken by vite for production.",
5
5
  "keywords": [
6
6
  "TypeScript",
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: unknown[]) => T;
25
+ type Constructor<T> = new (...args: any[]) => T;
26
26
 
27
27
  type AllJSTypes = PrimitiveTypeStrings | null | undefined | Constructor<unknown>;
28
28
 
@@ -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);
@@ -1,3 +0,0 @@
1
- ## [0.1.x to 0.2.0](https://github.com/OfficialHalfwayDead/assertie/compare/v0.1.0...v0.2.0)
2
-
3
- * Change all calls to `assertTypeof<<Something>>` to `assertTypeOf<<Something>>` (capitalization change for consistency).