0
# Utility Functions
1
2
Runtime functions that complement the type system, providing assertions, factories, and helper utilities. These are actual JavaScript functions that work at runtime alongside the TypeScript type system.
3
4
## Capabilities
5
6
### assert
7
8
Runtime function that helps assert `condition`. When `condition` is falsy, it throws an error with `Assertion Error: ${message}`.
9
10
```typescript { .api }
11
function assert(condition: any, message?: string): asserts condition;
12
```
13
14
**Usage Example:**
15
16
```typescript
17
import { assert } from "ts-essentials";
18
19
function processUser(user: unknown) {
20
assert(user && typeof user === "object", "User must be an object");
21
assert("id" in user, "User must have an id");
22
assert("name" in user, "User must have a name");
23
24
// TypeScript now knows user is an object with id and name properties
25
console.log(user.id, user.name); // Type-safe access
26
}
27
28
// Usage in validation
29
function divide(a: number, b: number): number {
30
assert(b !== 0, "Division by zero is not allowed");
31
return a / b; // Safe to divide since b is not 0
32
}
33
34
// Custom assertion messages
35
function validateConfig(config: any) {
36
assert(config, "Configuration is required");
37
assert(config.apiKey, "API key is missing from configuration");
38
assert(typeof config.timeout === "number", "Timeout must be a number");
39
}
40
```
41
42
### UnreachableCaseError
43
44
Runtime class instance type that helps check exhaustiveness for `value`. When `value` isn't `never`, it shows TypeScript error.
45
46
```typescript { .api }
47
class UnreachableCaseError extends Error {
48
constructor(value: never);
49
}
50
```
51
52
**Usage Example:**
53
54
```typescript
55
import { UnreachableCaseError } from "ts-essentials";
56
57
type Status = "loading" | "success" | "error";
58
59
function handleStatus(status: Status): string {
60
switch (status) {
61
case "loading":
62
return "Loading...";
63
case "success":
64
return "Success!";
65
case "error":
66
return "Error occurred";
67
default:
68
// This ensures exhaustive checking
69
throw new UnreachableCaseError(status);
70
}
71
}
72
73
// If you add a new status type, TypeScript will error at the UnreachableCaseError line
74
type ExtendedStatus = "loading" | "success" | "error" | "pending";
75
76
function handleExtendedStatus(status: ExtendedStatus): string {
77
switch (status) {
78
case "loading":
79
return "Loading...";
80
case "success":
81
return "Success!";
82
case "error":
83
return "Error occurred";
84
// Missing "pending" case
85
default:
86
// TypeScript error: Argument of type 'string' is not assignable to parameter of type 'never'
87
throw new UnreachableCaseError(status);
88
}
89
}
90
```
91
92
### createFactoryWithConstraint
93
94
Runtime function that validates that type of `value` matches `Constraint` without changing resulting type of `value`. This is a ponyfill for TypeScript's `satisfies` operator.
95
96
```typescript { .api }
97
function createFactoryWithConstraint<Constraint>(): <T extends Constraint>(value: T) => T;
98
```
99
100
**Usage Example:**
101
102
```typescript
103
import { createFactoryWithConstraint } from "ts-essentials";
104
105
// Define a constraint interface
106
interface ApiEndpoint {
107
path: string;
108
method: "GET" | "POST" | "PUT" | "DELETE";
109
handler: (req: any) => any;
110
}
111
112
// Create a factory that ensures objects satisfy the constraint
113
const createEndpoint = createFactoryWithConstraint<ApiEndpoint>();
114
115
// Usage - type is preserved while ensuring constraint satisfaction
116
const userEndpoint = createEndpoint({
117
path: "/users",
118
method: "GET",
119
handler: (req) => ({ users: [] }),
120
// Can include additional properties not in constraint
121
middleware: ["auth", "logging"]
122
});
123
124
// userEndpoint has the exact type including the middleware property
125
// But TypeScript ensures it satisfies the ApiEndpoint constraint
126
127
// Error example - missing required properties
128
// const invalidEndpoint = createEndpoint({
129
// path: "/invalid"
130
// // Error: Property 'method' is missing
131
// });
132
133
// Configuration validation example
134
interface DatabaseConfig {
135
host: string;
136
port: number;
137
database: string;
138
}
139
140
const createDbConfig = createFactoryWithConstraint<DatabaseConfig>();
141
142
const prodConfig = createDbConfig({
143
host: "prod-db.example.com",
144
port: 5432,
145
database: "myapp",
146
// Additional properties are preserved
147
ssl: true,
148
maxConnections: 100
149
});
150
```
151
152
### isExact
153
154
Runtime function that validates that type of `actual` equals to `Expected`. Otherwise shows TypeScript error.
155
156
```typescript { .api }
157
function isExact<Expected>(): <T>(actual: T) => T extends Expected
158
? Expected extends T
159
? T
160
: never
161
: never;
162
```
163
164
**Usage Example:**
165
166
```typescript
167
import { isExact } from "ts-essentials";
168
169
// Create exact type checker
170
const checkExactUser = isExact<{ id: number; name: string }>();
171
172
// Valid - exact match
173
const validUser = checkExactUser({ id: 1, name: "Alice" });
174
175
// Invalid - missing property (TypeScript error)
176
// const invalidUser1 = checkExactUser({ id: 1 }); // Error
177
178
// Invalid - extra property (TypeScript error)
179
// const invalidUser2 = checkExactUser({ id: 1, name: "Alice", email: "alice@example.com" }); // Error
180
181
// Usage in testing
182
function testExactType<T>(value: T, expected: T): void {
183
const checker = isExact<T>();
184
checker(value); // Ensures exact type match
185
}
186
187
// Configuration validation
188
interface AppConfig {
189
apiUrl: string;
190
timeout: number;
191
retries: number;
192
}
193
194
const validateExactConfig = isExact<AppConfig>();
195
196
function loadConfig(config: unknown): AppConfig {
197
// This ensures config matches AppConfig exactly
198
return validateExactConfig(config as AppConfig);
199
}
200
```
201
202
### noop
203
204
Runtime function that does nothing with arguments `_args`. Useful for default callbacks and placeholders.
205
206
```typescript { .api }
207
function noop(..._args: any[]): void;
208
```
209
210
**Usage Example:**
211
212
```typescript
213
import { noop } from "ts-essentials";
214
215
// Default callback parameter
216
interface Options {
217
onSuccess?: (data: any) => void;
218
onError?: (error: Error) => void;
219
}
220
221
function processData(data: any, options: Options = {}) {
222
const { onSuccess = noop, onError = noop } = options;
223
224
try {
225
const result = transformData(data);
226
onSuccess(result);
227
} catch (error) {
228
onError(error as Error);
229
}
230
}
231
232
// Event handler placeholder
233
class EventEmitter {
234
private handlers: Map<string, Function> = new Map();
235
236
on(event: string, handler: Function = noop) {
237
this.handlers.set(event, handler);
238
}
239
240
emit(event: string, ...args: any[]) {
241
const handler = this.handlers.get(event) || noop;
242
handler(...args);
243
}
244
}
245
246
// Development vs production logging
247
const logger = process.env.NODE_ENV === "production"
248
? { log: noop, error: noop, warn: noop }
249
: console;
250
251
logger.log("This only logs in development");
252
253
// Conditional operations
254
function setupFeature(enabled: boolean, callback: () => void = noop) {
255
if (enabled) {
256
callback();
257
}
258
}
259
260
setupFeature(false); // No error, noop is used as default
261
```
262
263
## Advanced Usage
264
265
Combine utility functions for robust error handling and type safety:
266
267
```typescript
268
import { assert, UnreachableCaseError, createFactoryWithConstraint, isExact, noop } from "ts-essentials";
269
270
// Comprehensive validation pipeline
271
interface UserData {
272
id: number;
273
email: string;
274
role: "admin" | "user" | "guest";
275
}
276
277
const createUser = createFactoryWithConstraint<UserData>();
278
const validateExactUser = isExact<UserData>();
279
280
function processUserData(data: unknown): UserData {
281
// Runtime assertion
282
assert(data && typeof data === "object", "Data must be an object");
283
assert("id" in data && typeof data.id === "number", "Invalid user ID");
284
assert("email" in data && typeof data.email === "string", "Invalid email");
285
assert("role" in data, "Role is required");
286
287
// Exact type validation
288
const validatedData = validateExactUser(data as UserData);
289
290
// Constraint satisfaction
291
return createUser(validatedData);
292
}
293
294
// Exhaustive pattern matching with error handling
295
function getUserPermissions(role: UserData["role"]): string[] {
296
switch (role) {
297
case "admin":
298
return ["read", "write", "delete", "manage"];
299
case "user":
300
return ["read", "write"];
301
case "guest":
302
return ["read"];
303
default:
304
throw new UnreachableCaseError(role);
305
}
306
}
307
308
// Safe callback execution
309
function executeCallback<T extends any[]>(
310
callback: ((...args: T) => void) | undefined,
311
...args: T
312
): void {
313
const safeCallback = callback || noop;
314
try {
315
safeCallback(...args);
316
} catch (error) {
317
console.error("Callback execution failed:", error);
318
}
319
}
320
321
// Type-safe factory with validation
322
function createValidatedFactory<T>(
323
validator: (value: unknown) => value is T
324
) {
325
const factory = createFactoryWithConstraint<T>();
326
327
return function(value: unknown): T {
328
assert(validator(value), "Validation failed");
329
return factory(value as T);
330
};
331
}
332
```