assertie 0.3.2 → 1.0.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 +24 -1
- package/MIGRATION-GUIDE.md +13 -0
- package/README.md +18 -6
- package/lib/assert-helpers.d.ts +20 -0
- package/lib/assert-helpers.js +66 -0
- package/lib/index.d.ts +49 -56
- package/lib/index.js +56 -117
- package/lib/types.d.ts +29 -0
- package/lib/types.js +1 -0
- package/package.json +9 -4
- package/src/assert-helpers.ts +63 -0
- package/src/index.ts +146 -143
- package/src/types.ts +39 -0
- package/test/runtime/assert-helpers.test.ts +583 -0
- package/test/runtime/asserts.test.ts +641 -0
- package/test/runtime/main.ts +6 -0
- package/test/runtime/testing.ts +168 -0
- package/test/runtime/tsconfig.json +18 -0
- package/test/types/readonly.5+.test.ts +44 -0
- package/test/types/readonly.test.ts +238 -0
- package/test/types/run.ts +29 -0
- package/test/types/tsconfig.4.x.json +7 -0
- package/test/types/tsconfig.json +18 -0
- package/test/types/type-narrowing.5+.test.ts +32 -0
- package/test/types/type-narrowing.test.ts +654 -0
- package/tsconfig.json +1 -0
package/lib/index.js
CHANGED
|
@@ -1,319 +1,258 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
if (typeof expectedType === "string")
|
|
7
|
-
return expectedType;
|
|
8
|
-
return expectedType.name;
|
|
9
|
-
}
|
|
10
|
-
function getTypeNameOfUnknown(item) {
|
|
11
|
-
if (item === null)
|
|
12
|
-
return "null";
|
|
13
|
-
if (item === undefined)
|
|
14
|
-
return "undefined";
|
|
15
|
-
try {
|
|
16
|
-
if (item instanceof item.constructor && item.constructor.name !== "Function") {
|
|
17
|
-
// I'd like function to match the primitive name "function"
|
|
18
|
-
// because that's how the asserts are written.
|
|
19
|
-
return item.constructor.name;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
finally {
|
|
23
|
-
return typeof item;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
function isType(item, expectedType) {
|
|
27
|
-
if (typeof item === expectedType)
|
|
28
|
-
return true;
|
|
29
|
-
const reducedExpectedType = expectedType;
|
|
30
|
-
if (item === reducedExpectedType)
|
|
31
|
-
return true;
|
|
32
|
-
const remainingOption = expectedType;
|
|
33
|
-
if (item instanceof remainingOption)
|
|
34
|
-
return true;
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
class AssertionError extends Error {
|
|
1
|
+
import { getNameOfExpectedType, getTypeNameOfUnknown, isType } from "./assert-helpers";
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown by all assertie assertions when they fail.
|
|
4
|
+
*/
|
|
5
|
+
export class AssertieError extends Error {
|
|
38
6
|
constructor(msg) {
|
|
39
7
|
super(`Assertion failed: ${msg}`);
|
|
40
|
-
this.name =
|
|
8
|
+
this.name = AssertieError.name;
|
|
41
9
|
}
|
|
42
10
|
}
|
|
43
11
|
/**
|
|
44
12
|
* Asserts that the provided boolean is true.
|
|
45
13
|
* @param {boolean} hasToBeTrue - The boolean to assert.
|
|
46
14
|
* @param {string} msg - The message of the Error if the assertion fails.
|
|
47
|
-
* @throws {
|
|
15
|
+
* @throws {AssertieError} if the assertion fails.
|
|
48
16
|
*/
|
|
49
17
|
export function assert(hasToBeTrue, msg = "No specific message provided.") {
|
|
50
18
|
if (!import.meta.env.DEV)
|
|
51
19
|
return;
|
|
52
20
|
if (!hasToBeTrue)
|
|
53
|
-
throw new
|
|
21
|
+
throw new AssertieError(msg);
|
|
54
22
|
}
|
|
55
23
|
/**
|
|
56
|
-
* Asserts that the provided
|
|
57
|
-
* @param {unknown} item - The
|
|
58
|
-
* @param {AllJSTypes} expectedType - The expected type of the
|
|
59
|
-
* @throws {
|
|
24
|
+
* Asserts that the provided item is of the expectedType.
|
|
25
|
+
* @param {unknown} item - The item which ought to be of the expectedType.
|
|
26
|
+
* @param {AllJSTypes} expectedType - The expected type of the item. JS primitive types, null, undefined, and constructable types are supported. JS primitive types are passed as the string they return from typeof, e.g., "number".
|
|
27
|
+
* @throws {AssertieError} if the type isn't as expected.
|
|
60
28
|
*/
|
|
61
29
|
export function assertType(item, expectedType) {
|
|
62
30
|
if (!import.meta.env.DEV)
|
|
63
31
|
return;
|
|
64
32
|
if (!isType(item, expectedType))
|
|
65
|
-
throw new
|
|
33
|
+
throw new AssertieError(`Provided object was not of type ${getNameOfExpectedType(expectedType)}. Was: ${getTypeNameOfUnknown(item)}, value: ${item}`);
|
|
66
34
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Asserts that all elements of the provided array are of the expected type. It ensures that the array is not sparse (even when the expectedType is undefined).
|
|
69
|
-
* @param {unknown[]} arr - The array which ought to be an array of the expectedType, i.e. expectedType: "number" => arr: number[]
|
|
70
|
-
* @param {AllJSTypes} expectedType - The expected type of individual items. JS primitive types, null, undefined, and constructable types are supported.
|
|
71
|
-
* @throws {AssertionError} if the type isn't as expected.
|
|
72
|
-
*/
|
|
73
35
|
export function assertArrayType(arr, expectedType) {
|
|
74
36
|
if (!import.meta.env.DEV)
|
|
75
37
|
return;
|
|
76
38
|
for (let i = 0; i < arr.length; i++) {
|
|
77
39
|
if (!(i in arr))
|
|
78
|
-
throw new
|
|
40
|
+
throw new AssertieError(`Array to assert type of was sparse with a missing item at index ${i}`);
|
|
79
41
|
const item = arr[i];
|
|
80
42
|
if (!isType(item, expectedType))
|
|
81
|
-
throw new
|
|
43
|
+
throw new AssertieError(`Provided array had item at index ${i} not of type ${getNameOfExpectedType(expectedType)}. Was: ${getTypeNameOfUnknown(item)}, value: ${item}`);
|
|
82
44
|
}
|
|
83
45
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Asserts that the array or tuple has the expected types at each index.
|
|
86
|
-
* @param {unknown[] | [unknown, ...]} arrayOrTuple - The tuple which ought to be an array of the length and types.
|
|
87
|
-
* @param {[AllJSTypes, ...]} expectedTypes - A tuple of expected types of individual items, e.g., expectedTypes = ["number", "string", Date] => arrayOrTuple: [number, string, Date]. The individual entries can be JS primitive types, null, undefined, and constructors.
|
|
88
|
-
* @throws {AssertionError} if the type of any element of the tuple isn't as expected.
|
|
89
|
-
*/
|
|
90
46
|
export function assertTupleTypes(arrayOrTuple, expectedTypes) {
|
|
91
47
|
if (!import.meta.env.DEV)
|
|
92
48
|
return;
|
|
93
49
|
if (arrayOrTuple.length !== expectedTypes.length) {
|
|
94
|
-
throw new
|
|
50
|
+
throw new AssertieError(`Provided tuple length mismatch: expected ${expectedTypes.length}, but got ${arrayOrTuple.length}`);
|
|
95
51
|
}
|
|
96
52
|
for (let i = 0; i < expectedTypes.length; i++) {
|
|
97
53
|
if (!(i in arrayOrTuple))
|
|
98
|
-
throw new
|
|
54
|
+
throw new AssertieError(`Provided tuple was sparse with a missing item at required index ${i}`);
|
|
99
55
|
const item = arrayOrTuple[i];
|
|
100
56
|
if (!isType(item, expectedTypes[i])) {
|
|
101
|
-
throw new
|
|
57
|
+
throw new AssertieError(`Provided tuple had item at index ${i} not of type ${getNameOfExpectedType(expectedTypes[i])}. Was: ${getTypeNameOfUnknown(item)}, value: ${item}`);
|
|
102
58
|
}
|
|
103
59
|
}
|
|
104
60
|
}
|
|
105
61
|
/**
|
|
106
62
|
* Asserts that the provided item is of type string.
|
|
107
63
|
* @param {unknown} item - The item which ought to be of type string.
|
|
108
|
-
* @throws {
|
|
64
|
+
* @throws {AssertieError} if the type isn't string.
|
|
109
65
|
*/
|
|
110
66
|
export function assertTypeOfString(item) {
|
|
111
67
|
if (!import.meta.env.DEV)
|
|
112
68
|
return;
|
|
113
69
|
if (typeof item !== "string")
|
|
114
|
-
throw new
|
|
70
|
+
throw new AssertieError(`Provided item was not of type string. Was: ${getTypeNameOfUnknown(item)}`);
|
|
115
71
|
}
|
|
116
72
|
/**
|
|
117
73
|
* Asserts that the provided item is of type number.
|
|
118
74
|
* @param {unknown} item - The item which ought to be of type number.
|
|
119
|
-
* @throws {
|
|
75
|
+
* @throws {AssertieError} if the type isn't number.
|
|
120
76
|
*/
|
|
121
77
|
export function assertTypeOfNumber(item) {
|
|
122
78
|
if (!import.meta.env.DEV)
|
|
123
79
|
return;
|
|
124
80
|
if (typeof item !== "number")
|
|
125
|
-
throw new
|
|
81
|
+
throw new AssertieError(`Provided item was not of type number. Was: ${getTypeNameOfUnknown(item)}`);
|
|
126
82
|
}
|
|
127
83
|
/**
|
|
128
84
|
* Asserts that the provided item is of type boolean.
|
|
129
85
|
* @param {unknown} item - The item which ought to be of type boolean.
|
|
130
|
-
* @throws {
|
|
86
|
+
* @throws {AssertieError} if the type isn't boolean.
|
|
131
87
|
*/
|
|
132
88
|
export function assertTypeOfBoolean(item) {
|
|
133
89
|
if (!import.meta.env.DEV)
|
|
134
90
|
return;
|
|
135
91
|
if (typeof item !== "boolean")
|
|
136
|
-
throw new
|
|
92
|
+
throw new AssertieError(`Provided item was not of type boolean. Was: ${getTypeNameOfUnknown(item)}`);
|
|
137
93
|
}
|
|
138
94
|
/**
|
|
139
95
|
* Asserts that the provided item is of type bigint.
|
|
140
96
|
* @param {unknown} item - The item which ought to be of type bigint.
|
|
141
|
-
* @throws {
|
|
97
|
+
* @throws {AssertieError} if the type isn't bigint.
|
|
142
98
|
*/
|
|
143
99
|
export function assertTypeOfBigint(item) {
|
|
144
100
|
if (!import.meta.env.DEV)
|
|
145
101
|
return;
|
|
146
102
|
if (typeof item !== "bigint")
|
|
147
|
-
throw new
|
|
103
|
+
throw new AssertieError(`Provided item was not of type bigint. Was: ${getTypeNameOfUnknown(item)}`);
|
|
148
104
|
}
|
|
149
105
|
/**
|
|
150
106
|
* Asserts that the provided item is of type undefined.
|
|
151
107
|
* @param {unknown} item - The item which ought to be of type undefined.
|
|
152
|
-
* @throws {
|
|
108
|
+
* @throws {AssertieError} if the type isn't undefined.
|
|
153
109
|
*/
|
|
154
110
|
export function assertTypeOfUndefined(item) {
|
|
155
111
|
if (!import.meta.env.DEV)
|
|
156
112
|
return;
|
|
157
113
|
if (typeof item !== "undefined")
|
|
158
|
-
throw new
|
|
114
|
+
throw new AssertieError(`Provided item was not of type undefined. Was: ${getTypeNameOfUnknown(item)}`);
|
|
159
115
|
}
|
|
160
116
|
/**
|
|
161
117
|
* Asserts that the provided item is of type function.
|
|
162
118
|
* @param {unknown} item - The item which ought to be of type function.
|
|
163
|
-
* @throws {
|
|
119
|
+
* @throws {AssertieError} if the type isn't function.
|
|
164
120
|
*/
|
|
165
121
|
export function assertTypeOfFunction(item) {
|
|
166
122
|
if (!import.meta.env.DEV)
|
|
167
123
|
return;
|
|
168
124
|
if (typeof item !== "function")
|
|
169
|
-
throw new
|
|
125
|
+
throw new AssertieError(`Provided item was not of type function. Was: ${getTypeNameOfUnknown(item)}`);
|
|
170
126
|
}
|
|
171
127
|
/**
|
|
172
128
|
* Asserts that the provided item is of type object.
|
|
173
129
|
* @param {unknown} item - The item which ought to be of type object.
|
|
174
|
-
* @throws {
|
|
130
|
+
* @throws {AssertieError} if the type isn't object.
|
|
175
131
|
*/
|
|
176
132
|
export function assertTypeOfObject(item) {
|
|
177
133
|
if (!import.meta.env.DEV)
|
|
178
134
|
return;
|
|
179
135
|
if (typeof item !== "object")
|
|
180
|
-
throw new
|
|
136
|
+
throw new AssertieError(`Provided item was not of type object. Was: ${getTypeNameOfUnknown(item)}`);
|
|
181
137
|
}
|
|
182
138
|
/**
|
|
183
139
|
* Asserts that the provided item is of type symbol.
|
|
184
140
|
* @param {unknown} item - The item which ought to be of type symbol.
|
|
185
|
-
* @throws {
|
|
141
|
+
* @throws {AssertieError} if the type isn't symbol.
|
|
186
142
|
*/
|
|
187
143
|
export function assertTypeOfSymbol(item) {
|
|
188
144
|
if (!import.meta.env.DEV)
|
|
189
145
|
return;
|
|
190
146
|
if (typeof item !== "symbol")
|
|
191
|
-
throw new
|
|
147
|
+
throw new AssertieError(`Provided item was not of type symbol. Was: ${getTypeNameOfUnknown(item)}`);
|
|
192
148
|
}
|
|
193
149
|
/**
|
|
194
150
|
* Asserts that the provided item is null.
|
|
195
151
|
* @param {unknown} item - The item which ought to be null.
|
|
196
|
-
* @throws {
|
|
152
|
+
* @throws {AssertieError} if the value isn't null.
|
|
197
153
|
*/
|
|
198
154
|
export function assertNull(item) {
|
|
199
155
|
if (!import.meta.env.DEV)
|
|
200
156
|
return;
|
|
201
157
|
if (item !== null)
|
|
202
|
-
throw new
|
|
158
|
+
throw new AssertieError(`Provided item was not null. Was type: ${getTypeNameOfUnknown(item)}, value: ${item}`);
|
|
203
159
|
}
|
|
204
160
|
/**
|
|
205
161
|
* Asserts that the provided item is an instance of the provided constructor.
|
|
206
162
|
* @param {unknown} item - The item which ought to be an instance of the constructor.
|
|
207
163
|
* @param {Constructor<T>} constructor - Anything that can be after an instanceof operator.
|
|
208
|
-
* @throws {
|
|
164
|
+
* @throws {AssertieError} if item instanceof constructor is false.
|
|
209
165
|
*/
|
|
210
166
|
export function assertInstanceOf(item, constructor) {
|
|
211
167
|
if (!import.meta.env.DEV)
|
|
212
168
|
return;
|
|
213
169
|
if (!(item instanceof constructor))
|
|
214
|
-
throw new
|
|
170
|
+
throw new AssertieError(`Provided item was not of type ${constructor.name} but was type: ${getTypeNameOfUnknown(item)}, value: ${item}`);
|
|
215
171
|
}
|
|
216
|
-
/**
|
|
217
|
-
* Asserts that the provided array is a tuple of exactly the expected length.
|
|
218
|
-
* @param {unknown[]} arr - The array which ought to be a tuple.
|
|
219
|
-
* @param {number} expectedLength - The exact expected length of the tuple.
|
|
220
|
-
* @throws {AssertionError} if the array isn't of the expected length or is sparse.
|
|
221
|
-
*/
|
|
222
172
|
export function assertIsTuple(arr, expectedLength) {
|
|
223
173
|
if (!import.meta.env.DEV)
|
|
224
174
|
return;
|
|
225
175
|
if (arr.length !== expectedLength) {
|
|
226
|
-
throw new
|
|
176
|
+
throw new AssertieError(`Provided array is not a tuple of expected length ${expectedLength}. It has length ${arr.length}.`);
|
|
227
177
|
}
|
|
228
178
|
for (let i = 0; i < expectedLength; i++) {
|
|
229
179
|
if (!(i in arr))
|
|
230
|
-
throw new
|
|
180
|
+
throw new AssertieError(`Provided tuple is sparse and therefore not a tuple. Index ${i} is missing.`);
|
|
231
181
|
}
|
|
232
182
|
}
|
|
233
183
|
/**
|
|
234
184
|
* Used to assert that code can never be reached. Pass a value which has already been checked for all types that should be possible. If the range of possible values increases, TypeScript will throw an error at compile time because the value won't be of type never.
|
|
235
185
|
* @param {never} item - An exhausted value, of which all cases are accounted for in other branches of the code, such as at the end of a switch statement.
|
|
236
186
|
* @param {string} msg - Override the default error message. Even if you do, the error message will include the value and type of item.
|
|
237
|
-
* @throws {
|
|
187
|
+
* @throws {AssertieError} if at runtime the function call was reached. This should only happen if TypeScript types are inaccurate somewhere.
|
|
238
188
|
*/
|
|
239
189
|
export function assertUnreachable(item, msg = "Unreachable code of type never was reached. TypeScript types are inaccurate somewhere.") {
|
|
240
190
|
if (!import.meta.env.DEV)
|
|
241
191
|
return;
|
|
242
|
-
throw new
|
|
243
|
-
`\nValue of type never was actually of type: ${getTypeNameOfUnknown(item)}, value: ${item}`);
|
|
192
|
+
throw new AssertieError(msg + `\nValue of type never was actually of type: ${getTypeNameOfUnknown(item)}, value: ${item}`);
|
|
244
193
|
}
|
|
245
194
|
/**
|
|
246
195
|
* Asserts that the provided item is neither null nor undefined.
|
|
247
196
|
* @param {unknown} item - The item which ought to be non-null.
|
|
248
|
-
* @throws {
|
|
197
|
+
* @throws {AssertieError} if the item is null or undefined.
|
|
249
198
|
*/
|
|
250
199
|
export function assertNonNullable(item) {
|
|
251
200
|
if (!import.meta.env.DEV)
|
|
252
201
|
return;
|
|
253
202
|
if (item === undefined || item === null)
|
|
254
|
-
throw new
|
|
203
|
+
throw new AssertieError(`Provided item should've been non-null but was: ${item}`);
|
|
255
204
|
}
|
|
256
205
|
/**
|
|
257
206
|
* Asserts that the provided object has non-null values for the properties passed as keys in the propKeys array.
|
|
258
207
|
* @param {object} obj - The object which ought to have the properties.
|
|
259
208
|
* @param {NullableKeys<T>} propKeys - An array of the stringified keys of the properties which ought to be non-null in the object.
|
|
260
|
-
* @throws {
|
|
209
|
+
* @throws {AssertieError} if any of the properties was null, undefined, or not present in the object.
|
|
261
210
|
*/
|
|
262
211
|
export function assertPropsNonNullable(obj, propKeys) {
|
|
263
212
|
if (!import.meta.env.DEV)
|
|
264
213
|
return;
|
|
265
214
|
for (const propKey of propKeys) {
|
|
266
215
|
if (!(propKey in obj))
|
|
267
|
-
throw new
|
|
216
|
+
throw new AssertieError(`Provided object prop ${String(propKey)} should've been non-null but was not present at all.`);
|
|
268
217
|
if (obj[propKey] === null || obj[propKey] === undefined)
|
|
269
|
-
throw new
|
|
218
|
+
throw new AssertieError(`Provided object prop ${String(propKey)} should've been non-null but was: ${obj[propKey]}`);
|
|
270
219
|
}
|
|
271
220
|
}
|
|
272
|
-
/**
|
|
273
|
-
* Asserts that all elements of the provided array are neither null nor undefined, or not present.
|
|
274
|
-
* @param {unknown[]} arr - The array which ought to be non-sparse, and have only non-null elements.
|
|
275
|
-
* @throws {AssertionError} if any of the elements was null, undefined, or not present in the array.
|
|
276
|
-
*/
|
|
277
221
|
export function assertArrayNonNullable(arr) {
|
|
278
222
|
if (!import.meta.env.DEV)
|
|
279
223
|
return;
|
|
280
224
|
for (let i = 0; i < arr.length; i++) {
|
|
281
225
|
if (!(i in arr))
|
|
282
|
-
throw new
|
|
226
|
+
throw new AssertieError(`Provided array should've been non-null but was sparse with a missing item at index ${i}`);
|
|
283
227
|
const item = arr[i];
|
|
284
228
|
if (item === null)
|
|
285
|
-
throw new
|
|
229
|
+
throw new AssertieError(`Provided array should've been non-null but had an item with value null at index ${i}`);
|
|
286
230
|
if (item === undefined)
|
|
287
|
-
throw new
|
|
231
|
+
throw new AssertieError(`Provided array should've been non-null but had an undefined item at index ${i}`);
|
|
288
232
|
}
|
|
289
233
|
}
|
|
290
|
-
/**
|
|
291
|
-
* Asserts that the provided tuple has non-null values for all elements. This function does not take a length. So if you want to assert that the typescript tuple type is of the correct length, call @see assertIsTuple first.
|
|
292
|
-
* @param {[unknown, ...]} tuple - The tuple which ought to have only non-null values.
|
|
293
|
-
* @throws {AssertionError} if any of the elements was null, undefined, or an index not present in the tuple.
|
|
294
|
-
*/
|
|
295
234
|
export function assertTupleNonNullable(tuple) {
|
|
296
235
|
if (!import.meta.env.DEV)
|
|
297
236
|
return;
|
|
298
237
|
for (let i = 0; i < tuple.length; i++) {
|
|
299
238
|
if (!(i in tuple))
|
|
300
|
-
throw new
|
|
239
|
+
throw new AssertieError(`Provided tuple should've been non-null but is sparse. Index ${i} is missing.`);
|
|
301
240
|
if (tuple[i] === null)
|
|
302
|
-
throw new
|
|
241
|
+
throw new AssertieError(`Provided tuple should've been non-null but had an item with value null at index ${i}`);
|
|
303
242
|
if (tuple[i] === undefined)
|
|
304
|
-
throw new
|
|
243
|
+
throw new AssertieError(`Provided tuple should've been non-null but had an undefined item at index ${i}`);
|
|
305
244
|
}
|
|
306
245
|
}
|
|
307
246
|
/**
|
|
308
247
|
* Asserts that the provided item is a finite number. Use to prevent NaN propagation.
|
|
309
248
|
* @param {unknown} item - The item which ought to be a finite number.
|
|
310
|
-
* @throws {
|
|
249
|
+
* @throws {AssertieError} if the item is not of type number, or isFinite(item) is false, i.e., if the item is NaN, Infinity, or -Infinity.
|
|
311
250
|
*/
|
|
312
251
|
export function assertFiniteNumber(item) {
|
|
313
252
|
if (!import.meta.env.DEV)
|
|
314
253
|
return;
|
|
315
254
|
if (typeof item !== "number")
|
|
316
|
-
throw new
|
|
255
|
+
throw new AssertieError(`Provided item was not of type number. Was: ${getTypeNameOfUnknown(item)}`);
|
|
317
256
|
if (!isFinite(item))
|
|
318
|
-
throw new
|
|
257
|
+
throw new AssertieError(`Provided number was not finite. Was: ${item}`);
|
|
319
258
|
}
|
package/lib/types.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type UnknownFunction = (...args: any[]) => unknown;
|
|
2
|
+
export type Constructor<T> = abstract new (...args: any[]) => T;
|
|
3
|
+
export type PrimitiveTypes = {
|
|
4
|
+
"string": string;
|
|
5
|
+
"number": number;
|
|
6
|
+
"boolean": boolean;
|
|
7
|
+
"bigint": bigint;
|
|
8
|
+
"undefined": undefined;
|
|
9
|
+
"function": UnknownFunction;
|
|
10
|
+
"object": object;
|
|
11
|
+
"symbol": symbol;
|
|
12
|
+
};
|
|
13
|
+
export type PrimitiveTypeStrings = keyof PrimitiveTypes;
|
|
14
|
+
export type NullableKeys<T> = {
|
|
15
|
+
[K in keyof T]-?: undefined extends T[K] ? K : null extends T[K] ? K : never;
|
|
16
|
+
}[keyof T];
|
|
17
|
+
export type PropsNonNullable<T, N extends NullableKeys<T>> = T & {
|
|
18
|
+
[K in N]-?: NonNullable<T[K]>;
|
|
19
|
+
};
|
|
20
|
+
export type AllJSTypes = PrimitiveTypeStrings | null | undefined | Constructor<unknown>;
|
|
21
|
+
export 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
|
+
export type ResolveTuple<T extends readonly AllJSTypes[], U> = U & {
|
|
23
|
+
[K in keyof T]: ResolveAnyJSType<T[K]>;
|
|
24
|
+
};
|
|
25
|
+
export type ResolveReadonlyTuple<T extends readonly AllJSTypes[], U> = U & {
|
|
26
|
+
readonly [K in keyof T]: ResolveAnyJSType<T[K]>;
|
|
27
|
+
};
|
|
28
|
+
export type Tuple<T, N extends number, A extends unknown[] = []> = A["length"] extends N ? A : Tuple<T, N, [...A, T]>;
|
|
29
|
+
export type ReadonlyTuple<T, N extends number, A extends readonly unknown[] = readonly []> = A["length"] extends N ? A : ReadonlyTuple<T, N, readonly [...A, T]>;
|
package/lib/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assertie",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Debug assertions for TypeScript, auto tree-shaken by vite for production.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"TypeScript",
|
|
@@ -33,10 +33,15 @@
|
|
|
33
33
|
"vite": ">=2.0.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
36
|
+
"@types/node": "24.10.11",
|
|
37
|
+
"typescript": "4.7.4",
|
|
38
|
+
"vite": "2.0.5"
|
|
38
39
|
},
|
|
39
40
|
"scripts": {
|
|
40
|
-
"build": "tsc"
|
|
41
|
+
"build": "tsc",
|
|
42
|
+
"test": "npm run test:runtime && npm run test:types",
|
|
43
|
+
"test:runtime": "npm run test:runtime:check && vite build --mode development -c test/runtime/vite.config.ts && node .test-build/test.js",
|
|
44
|
+
"test:runtime:check": "tsc -p test/runtime/tsconfig.json",
|
|
45
|
+
"test:types": "node test/types/run.ts"
|
|
41
46
|
}
|
|
42
47
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { AllJSTypes, ResolveAnyJSType, PrimitiveTypeStrings } from "./types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Gets the display name of an expected type for error messages.
|
|
5
|
+
* @param {AllJSTypes} expectedType - The expected type value.
|
|
6
|
+
* @returns {string} The normalized name of the expected type.
|
|
7
|
+
*/
|
|
8
|
+
export function getNameOfExpectedType(expectedType: AllJSTypes): string {
|
|
9
|
+
if (expectedType === null) return "null";
|
|
10
|
+
if (expectedType === undefined) return "undefined";
|
|
11
|
+
if (typeof expectedType === "string") return expectedType;
|
|
12
|
+
return expectedType.name;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Gets the runtime type name of an unknown item for error messages.
|
|
17
|
+
* @param {unknown} item - The item whose runtime type name should be determined.
|
|
18
|
+
* @returns {string} The runtime type name of item.
|
|
19
|
+
*/
|
|
20
|
+
export function getTypeNameOfUnknown(item: unknown): string {
|
|
21
|
+
if (item === null) return "null";
|
|
22
|
+
const type = typeof item;
|
|
23
|
+
switch (type) {
|
|
24
|
+
case "object":
|
|
25
|
+
case "function":
|
|
26
|
+
try {
|
|
27
|
+
const unsafe: any = item; // We are using try catch for the fail cases
|
|
28
|
+
if (unsafe instanceof unsafe.constructor) {
|
|
29
|
+
return unsafe.constructor.name;
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
const typeStr = Object.prototype.toString.call(item);
|
|
34
|
+
return typeStr.slice(8, -1); // "[object Type]" -> "Type"
|
|
35
|
+
default:
|
|
36
|
+
return type;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Checks whether the provided item is of the expectedType.
|
|
42
|
+
* @param {unknown} item - The item to check.
|
|
43
|
+
* @param {AllJSTypes} expectedType - The expected type to check against.
|
|
44
|
+
* @returns {boolean} `true` if item's type matches expectedType.
|
|
45
|
+
*/
|
|
46
|
+
export function isType<T extends AllJSTypes>(item: unknown, expectedType: T): item is ResolveAnyJSType<T> {
|
|
47
|
+
if (typeof item === expectedType) return true; // correct primitive type
|
|
48
|
+
// Now, if the expectedType is a PrimitiveTypeString,
|
|
49
|
+
// the item is guaranteed to be of the wrong type since it didn't match the typeof check above
|
|
50
|
+
if (typeof expectedType === "string") return false;
|
|
51
|
+
const expectedUndefNullOrConstructor = expectedType as Exclude<typeof expectedType, PrimitiveTypeStrings>;
|
|
52
|
+
|
|
53
|
+
// The type restriction on T guarantees that item is now either undefined, null, or a constructor
|
|
54
|
+
if (item === expectedUndefNullOrConstructor) return true; // correct undefined, null, or constructor of itself
|
|
55
|
+
// i.e. const MyType = Date; isType(Date, Date) && isType(MyType, Date) are both true
|
|
56
|
+
if (expectedUndefNullOrConstructor === null || expectedUndefNullOrConstructor === undefined) return false;
|
|
57
|
+
const expectedConstructor = expectedType as Exclude<typeof expectedUndefNullOrConstructor, null | undefined>;
|
|
58
|
+
|
|
59
|
+
// Lastly, check if the item is an instance of the provided constructor
|
|
60
|
+
if (item instanceof expectedConstructor) return true;
|
|
61
|
+
|
|
62
|
+
return false;
|
|
63
|
+
}
|