0
# Generic Type Utilities
1
2
Generic type checking utilities including the core type detection function, factory functions for creating custom type guards, and generic instance validation. These are the most flexible and powerful functions in the is-what library.
3
4
## Capabilities
5
6
### Core Type Detection
7
8
Returns the object type of the given payload using `Object.prototype.toString`. This is the foundational utility used by most other functions in the library.
9
10
```typescript { .api }
11
/**
12
* Returns the object type of the given payload
13
* @param payload - Value to inspect
14
* @returns String representation of the object type
15
*/
16
function getType(payload: unknown): string;
17
```
18
19
**Usage Examples:**
20
21
```typescript
22
import { getType } from "is-what";
23
24
// Primitive types
25
getType("hello"); // "String"
26
getType(42); // "Number"
27
getType(true); // "Boolean"
28
getType(null); // "Null"
29
getType(undefined); // "Undefined"
30
getType(Symbol("test")); // "Symbol"
31
getType(123n); // "BigInt"
32
33
// Object types
34
getType({}); // "Object"
35
getType([]); // "Array"
36
getType(new Date()); // "Date"
37
getType(/regex/); // "RegExp"
38
getType(() => {}); // "Function"
39
getType(new Map()); // "Map"
40
getType(new Set()); // "Set"
41
getType(new Error()); // "Error"
42
43
// Custom classes
44
class MyClass {}
45
getType(new MyClass()); // "Object" (custom classes show as Object)
46
47
// Practical usage
48
function createTypeChecker(expectedType: string) {
49
return (value: unknown) => getType(value) === expectedType;
50
}
51
52
const isStringType = createTypeChecker("String");
53
isStringType("hello"); // true
54
isStringType(123); // false
55
56
// Debug utility
57
function debugType(value: unknown, label: string = "value") {
58
console.log(`${label}: ${getType(value)} = ${JSON.stringify(value)}`);
59
}
60
61
debugType("test", "input"); // "input: String = "test""
62
debugType([1, 2, 3], "array"); // "array: Array = [1,2,3]"
63
```
64
65
### Generic Type Validation
66
67
Generic check to verify payload is of a given type. Useful for runtime type checking against constructor functions.
68
69
```typescript { .api }
70
/**
71
* Does a generic check to check that the given payload is of a given type.
72
* In cases like Number, it will return true for NaN as NaN is a Number;
73
* It will, however, differentiate between object and null
74
* @param payload - Value to check
75
* @param type - Constructor function or class to check against
76
* @returns Type guard indicating if payload is of the specified type
77
* @throws {TypeError} Will throw type error if type is an invalid type
78
*/
79
function isType<T extends AnyFunction | AnyClass>(
80
payload: unknown,
81
type: T
82
): payload is T;
83
84
type AnyClass = new (...args: unknown[]) => unknown;
85
```
86
87
**Usage Examples:**
88
89
```typescript
90
import { isType } from "is-what";
91
92
// Built-in constructors
93
isType("hello", String); // true
94
isType(new String("hello"), String); // true
95
isType(42, Number); // true
96
isType(NaN, Number); // true (unlike isNumber())
97
isType([], Array); // true
98
isType(new Date(), Date); // true
99
100
// Custom classes
101
class MyClass {
102
constructor(public name: string) {}
103
}
104
105
class OtherClass {}
106
107
const instance = new MyClass("test");
108
isType(instance, MyClass); // true
109
isType(instance, OtherClass); // false
110
111
if (isType(value, Date)) {
112
// TypeScript knows value is Date
113
console.log(value.getFullYear());
114
}
115
116
// Practical usage
117
function validateType<T extends AnyClass>(
118
value: unknown,
119
expectedType: T,
120
fieldName: string
121
): T {
122
if (isType(value, expectedType)) {
123
return value;
124
}
125
126
throw new Error(
127
`${fieldName} must be of type ${expectedType.name}, got ${getType(value)}`
128
);
129
}
130
131
// Generic type checker factory
132
function createTypeChecker<T extends AnyClass>(type: T) {
133
return (value: unknown): value is T => {
134
try {
135
return isType(value, type);
136
} catch {
137
return false;
138
}
139
};
140
}
141
142
const isDateInstance = createTypeChecker(Date);
143
const isArrayInstance = createTypeChecker(Array);
144
145
// Error handling - function throws TypeError for invalid types
146
try {
147
isType("test", "NotAConstructor" as any); // Throws TypeError
148
} catch (error) {
149
console.log("Invalid type provided");
150
}
151
```
152
153
### Instance Detection
154
155
Checks if a value is an instance of a class or matches a class name. Useful when you want to check for types that may not be defined in the current scope.
156
157
```typescript { .api }
158
/**
159
* Checks if a value is an instance of a class or a class name.
160
* Useful when you want to check if a value is an instance of a class
161
* that may not be defined in the current scope.
162
* @param value - The value to recursively check
163
* @param classOrClassName - A class constructor or string class name
164
* @returns Type guard indicating if value is instance of the specified class
165
*/
166
function isInstanceOf<T extends AnyClass>(value: unknown, class_: T): value is T;
167
function isInstanceOf<K extends GlobalClassName>(
168
value: unknown,
169
className: K
170
): value is (typeof globalThis)[K];
171
function isInstanceOf(value: unknown, className: string): value is object;
172
function isInstanceOf(value: unknown, classOrClassName: AnyClass | string): boolean;
173
```
174
175
**Usage Examples:**
176
177
```typescript
178
import { isInstanceOf } from "is-what";
179
180
// Class constructor checking
181
class MyClass {
182
constructor(public name: string) {}
183
}
184
185
class ChildClass extends MyClass {
186
constructor(name: string, public age: number) {
187
super(name);
188
}
189
}
190
191
const parent = new MyClass("parent");
192
const child = new ChildClass("child", 25);
193
194
isInstanceOf(parent, MyClass); // true
195
isInstanceOf(child, MyClass); // true (inheritance)
196
isInstanceOf(child, ChildClass); // true
197
isInstanceOf(parent, ChildClass); // false
198
199
// String-based class name checking (useful for cross-context checks)
200
isInstanceOf(new Date(), "Date"); // true
201
isInstanceOf([], "Array"); // true
202
isInstanceOf({}, "Object"); // true
203
isInstanceOf(new Error(), "Error"); // true
204
205
// Browser-specific types (when available)
206
if (typeof OffscreenCanvas !== 'undefined') {
207
const canvas = new OffscreenCanvas(100, 100);
208
isInstanceOf(canvas, "OffscreenCanvas"); // true
209
}
210
211
// Practical usage - checking for types that might not exist
212
function handleCanvasLikeObject(obj: unknown) {
213
if (isInstanceOf(obj, "HTMLCanvasElement")) {
214
// Handle HTML canvas
215
return obj.getContext("2d");
216
}
217
218
if (isInstanceOf(obj, "OffscreenCanvas")) {
219
// Handle OffscreenCanvas (if available)
220
return obj.getContext("2d");
221
}
222
223
throw new Error("Expected canvas-like object");
224
}
225
226
// Inheritance-aware validation
227
function validateUserModel(obj: unknown) {
228
if (isInstanceOf(obj, MyClass)) {
229
// Works for MyClass and any subclass
230
return {
231
name: obj.name,
232
type: obj.constructor.name
233
};
234
}
235
236
throw new Error("Object must be instance of MyClass or its subclasses");
237
}
238
239
// Cross-frame/context checking (using string names)
240
function isArrayLike(value: unknown) {
241
return isInstanceOf(value, "Array") ||
242
(typeof value === "object" &&
243
value !== null &&
244
"length" in value);
245
}
246
```
247
248
### Factory Function for Union Types
249
250
A factory function that creates a function to check if the payload is one of the given types. Supports 2-5 type guards with proper TypeScript union type inference.
251
252
```typescript { .api }
253
/**
254
* A factory function that creates a function to check if the payload is one of the given types.
255
*/
256
function isOneOf<A, B extends A, C extends A>(
257
a: TypeGuard<A, B>,
258
b: TypeGuard<A, C>
259
): TypeGuard<A, B | C>;
260
261
function isOneOf<A, B extends A, C extends A, D extends A>(
262
a: TypeGuard<A, B>,
263
b: TypeGuard<A, C>,
264
c: TypeGuard<A, D>
265
): TypeGuard<A, B | C | D>;
266
267
function isOneOf<A, B extends A, C extends A, D extends A, E extends A>(
268
a: TypeGuard<A, B>,
269
b: TypeGuard<A, C>,
270
c: TypeGuard<A, D>,
271
d: TypeGuard<A, E>
272
): TypeGuard<A, B | C | D | E>;
273
274
function isOneOf<A, B extends A, C extends A, D extends A, E extends A, F extends A>(
275
a: TypeGuard<A, B>,
276
b: TypeGuard<A, C>,
277
c: TypeGuard<A, D>,
278
d: TypeGuard<A, E>,
279
e: TypeGuard<A, F>
280
): TypeGuard<A, B | C | D | E | F>;
281
282
type TypeGuard<A, B extends A> = (payload: A) => payload is B;
283
```
284
285
**Usage Examples:**
286
287
```typescript
288
import { isOneOf, isNull, isUndefined, isString, isNumber } from "is-what";
289
290
// Create composite type checkers
291
const isNullOrUndefined = isOneOf(isNull, isUndefined);
292
const isStringOrNumber = isOneOf(isString, isNumber);
293
294
// Usage
295
isNullOrUndefined(null); // true
296
isNullOrUndefined(undefined); // true
297
isNullOrUndefined("hello"); // false
298
299
isStringOrNumber("hello"); // true
300
isStringOrNumber(42); // true
301
isStringOrNumber(true); // false
302
303
// TypeScript type narrowing works correctly
304
function processValue(value: unknown) {
305
if (isNullOrUndefined(value)) {
306
// TypeScript knows value is null | undefined
307
console.log("No value provided");
308
return null;
309
}
310
311
if (isStringOrNumber(value)) {
312
// TypeScript knows value is string | number
313
return value.toString(); // Both types have toString()
314
}
315
316
return "Other type";
317
}
318
319
// Complex combinations
320
const isPrimitiveOrNull = isOneOf(
321
isString,
322
isNumber,
323
isNull
324
);
325
326
// Custom type guards with isOneOf
327
function isEven(value: unknown): value is number {
328
return isNumber(value) && value % 2 === 0;
329
}
330
331
function isOdd(value: unknown): value is number {
332
return isNumber(value) && value % 2 === 1;
333
}
334
335
const isEvenOrOdd = isOneOf(isEven, isOdd);
336
337
// Practical usage - form validation
338
const isValidId = isOneOf(isString, isNumber);
339
340
function validateUserId(id: unknown) {
341
if (isValidId(id)) {
342
// TypeScript knows id is string | number
343
return { valid: true, id: id.toString() };
344
}
345
346
return { valid: false, error: "ID must be string or number" };
347
}
348
349
// API response validation
350
import { isArray, isObject } from "is-what";
351
352
const isArrayOrObject = isOneOf(isArray, isPlainObject);
353
354
function processApiResponse(response: unknown) {
355
if (isArrayOrObject(response)) {
356
// TypeScript knows response is unknown[] | PlainObject
357
return JSON.stringify(response, null, 2);
358
}
359
360
throw new Error("API response must be array or object");
361
}
362
363
// Error handling combinations
364
import { isError } from "is-what";
365
366
const isErrorOrString = isOneOf(isError, isString);
367
368
function handleResult(result: unknown) {
369
if (isErrorOrString(result)) {
370
// TypeScript knows result is Error | string
371
const message = isError(result) ? result.message : result;
372
console.log("Error:", message);
373
return null;
374
}
375
376
return result; // Success case
377
}
378
```
379
380
## Combined Generic Utility Patterns
381
382
```typescript
383
import {
384
getType,
385
isType,
386
isInstanceOf,
387
isOneOf,
388
isString,
389
isNumber,
390
isFunction
391
} from "is-what";
392
393
// Advanced type analysis
394
function analyzeTypeComprehensively(value: unknown) {
395
const basicType = getType(value);
396
const analysis = { basicType, details: {} as any };
397
398
// Check against common constructors
399
const constructors = [String, Number, Boolean, Array, Object, Date, RegExp];
400
for (const ctor of constructors) {
401
try {
402
if (isType(value, ctor)) {
403
analysis.details[ctor.name] = true;
404
}
405
} catch {
406
// Skip invalid constructors
407
}
408
}
409
410
// Check inheritance patterns
411
if (value && typeof value === "object") {
412
analysis.details.constructor = value.constructor?.name || "Unknown";
413
analysis.details.hasPrototype = Object.getPrototypeOf(value) !== null;
414
}
415
416
return analysis;
417
}
418
419
// Generic validation system
420
class TypeValidator {
421
private validators = new Map<string, (value: unknown) => boolean>();
422
423
register(name: string, validator: (value: unknown) => boolean) {
424
this.validators.set(name, validator);
425
}
426
427
validate(value: unknown, typeName: string) {
428
const validator = this.validators.get(typeName);
429
if (!validator) {
430
throw new Error(`Unknown type validator: ${typeName}`);
431
}
432
433
return validator(value);
434
}
435
436
createCompositeValidator(...typeNames: string[]) {
437
const validators = typeNames.map(name => this.validators.get(name))
438
.filter(Boolean);
439
440
return (value: unknown) => validators.some(validator => validator!(value));
441
}
442
}
443
444
// Usage
445
const validator = new TypeValidator();
446
validator.register("string", isString);
447
validator.register("number", isNumber);
448
validator.register("function", isFunction);
449
450
const isStringOrNumber = validator.createCompositeValidator("string", "number");
451
452
// Type-safe object creation
453
function createTypedObject<T extends AnyClass>(
454
type: T,
455
...args: ConstructorParameters<T>
456
): InstanceType<T> {
457
const instance = new type(...args);
458
459
if (isInstanceOf(instance, type)) {
460
return instance as InstanceType<T>;
461
}
462
463
throw new Error(`Failed to create instance of ${type.name}`);
464
}
465
466
// Example usage
467
const analysis = analyzeTypeComprehensively(new Date());
468
// Returns: {
469
// basicType: "Date",
470
// details: {
471
// Date: true,
472
// Object: true,
473
// constructor: "Date",
474
// hasPrototype: true
475
// }
476
// }
477
478
class User {
479
constructor(public name: string, public age: number) {}
480
}
481
482
const user = createTypedObject(User, "Alice", 30);
483
// TypeScript knows user is User instance
484
console.log(user.name); // "Alice"
485
```
486
487
## Types
488
489
```typescript { .api }
490
type AnyClass = new (...args: unknown[]) => unknown;
491
type TypeGuard<A, B extends A> = (payload: A) => payload is B;
492
493
type GlobalClassName = {
494
[K in keyof typeof globalThis]: (typeof globalThis)[K] extends AnyClass ? K : never;
495
}[keyof typeof globalThis];
496
```