0
# Type System Utilities
1
2
## Overview
3
4
The Type System Utilities are the second largest collection of functions in ts-api-utils, providing comprehensive tools for analyzing, inspecting, and working with TypeScript's type system. These utilities are essential for building robust TypeScript tooling, linters, and code analysis tools that need to understand and manipulate type information.
5
6
TypeScript's type system is complex and hierarchical, with various type categories including intrinsic types (built-in primitives), literal types, object types, union/intersection types, and more specialized constructs. The type system utilities provide a systematic way to inspect and classify types, extract type information, and perform type-aware operations.
7
8
## Core Concepts
9
10
### Types
11
TypeScript represents types as instances of the `ts.Type` interface. Each type has flags (`ts.TypeFlags`) that indicate its category and characteristics. Types can be simple primitives like `string` or `number`, complex object types, or computed types like unions and intersections.
12
13
### Type Checking
14
The TypeScript compiler provides a `TypeChecker` that can resolve types at specific locations in the AST, compare types, and provide detailed type information. Many utilities work in conjunction with the type checker to provide accurate analysis.
15
16
### Type Guards
17
Type guards are functions that narrow the type of a value through runtime checks, allowing TypeScript to understand more specific type information in conditional blocks. The utilities provide numerous type guards for different type categories.
18
19
## Type Getters
20
21
Functions that extract or derive type information from TypeScript types.
22
23
### getCallSignaturesOfType
24
25
Get the call signatures of a given type, handling union and intersection types appropriately.
26
27
```typescript { .api }
28
function getCallSignaturesOfType(type: ts.Type): readonly ts.Signature[]
29
```
30
31
**Parameters:**
32
- `type: ts.Type` - The type to get call signatures from
33
34
**Returns:** Array of call signatures. For union types, returns all signatures from all union members. For intersection types, returns signatures only if exactly one member has signatures.
35
36
**Example:**
37
```typescript
38
declare const type: ts.Type;
39
40
const signatures = getCallSignaturesOfType(type);
41
for (const signature of signatures) {
42
// Process each call signature
43
}
44
```
45
46
### getPropertyOfType
47
48
Get a specific property symbol from a type by name, including support for well-known symbols.
49
50
```typescript { .api }
51
function getPropertyOfType(type: ts.Type, name: ts.__String): ts.Symbol | undefined
52
```
53
54
**Parameters:**
55
- `type: ts.Type` - The type to search for the property
56
- `name: ts.__String` - The escaped property name to look for
57
58
**Returns:** The symbol representing the property, or `undefined` if not found
59
60
**Example:**
61
```typescript
62
declare const property: ts.Symbol;
63
declare const type: ts.Type;
64
65
const symbol = getPropertyOfType(type, property.getEscapedName());
66
if (symbol) {
67
// Property exists on the type
68
}
69
```
70
71
### getWellKnownSymbolPropertyOfType
72
73
Retrieve a type symbol corresponding to a well-known symbol name (like `Symbol.iterator`).
74
75
```typescript { .api }
76
function getWellKnownSymbolPropertyOfType(
77
type: ts.Type,
78
wellKnownSymbolName: string,
79
typeChecker: ts.TypeChecker
80
): ts.Symbol | undefined
81
```
82
83
**Parameters:**
84
- `type: ts.Type` - The type to search in
85
- `wellKnownSymbolName: string` - The well-known symbol name (e.g., "asyncIterator", "iterator")
86
- `typeChecker: ts.TypeChecker` - The type checker for resolving symbols
87
88
**Returns:** The symbol for the well-known symbol property, or `undefined` if not found
89
90
**Example:**
91
```typescript
92
declare const type: ts.Type;
93
declare const typeChecker: ts.TypeChecker;
94
95
const asyncIteratorSymbol = getWellKnownSymbolPropertyOfType(
96
type,
97
"asyncIterator",
98
typeChecker
99
);
100
```
101
102
## Type Guards
103
104
### Intrinsic Type Guards
105
106
Functions that check for TypeScript's built-in intrinsic types.
107
108
#### Type Definitions
109
110
```typescript { .api }
111
interface IntrinsicType extends ts.Type {
112
intrinsicName: string;
113
objectFlags: ts.ObjectFlags;
114
}
115
116
interface IntrinsicAnyType extends IntrinsicType {
117
intrinsicName: "any";
118
}
119
120
interface IntrinsicBigIntType extends IntrinsicType {
121
intrinsicName: "bigint";
122
}
123
124
interface IntrinsicBooleanType extends IntrinsicType {
125
intrinsicName: "boolean";
126
}
127
128
interface IntrinsicErrorType extends IntrinsicType {
129
intrinsicName: "error";
130
}
131
132
interface IntrinsicESSymbolType extends IntrinsicType {
133
intrinsicName: "symbol";
134
}
135
136
interface IntrinsicNeverType extends IntrinsicType {
137
intrinsicName: "never";
138
}
139
140
interface IntrinsicNonPrimitiveType extends IntrinsicType {
141
intrinsicName: "";
142
}
143
144
interface IntrinsicNullType extends IntrinsicType {
145
intrinsicName: "null";
146
}
147
148
interface IntrinsicNumberType extends IntrinsicType {
149
intrinsicName: "number";
150
}
151
152
interface IntrinsicStringType extends IntrinsicType {
153
intrinsicName: "string";
154
}
155
156
interface IntrinsicUndefinedType extends IntrinsicType {
157
intrinsicName: "undefined";
158
}
159
160
interface IntrinsicUnknownType extends IntrinsicType {
161
intrinsicName: "unknown";
162
}
163
164
interface IntrinsicVoidType extends IntrinsicType {
165
intrinsicName: "void";
166
}
167
```
168
169
#### isIntrinsicType
170
171
Test if a type is any intrinsic (built-in) type.
172
173
```typescript { .api }
174
function isIntrinsicType(type: ts.Type): type is IntrinsicType
175
```
176
177
#### isIntrinsicAnyType
178
179
Test if a type is the `any` intrinsic type.
180
181
```typescript { .api }
182
function isIntrinsicAnyType(type: ts.Type): type is IntrinsicAnyType
183
```
184
185
#### isIntrinsicBigIntType
186
187
Test if a type is the `bigint` intrinsic type.
188
189
```typescript { .api }
190
function isIntrinsicBigIntType(type: ts.Type): type is IntrinsicBigIntType
191
```
192
193
#### isIntrinsicBooleanType
194
195
Test if a type is the `boolean` intrinsic type.
196
197
```typescript { .api }
198
function isIntrinsicBooleanType(type: ts.Type): type is IntrinsicBooleanType
199
```
200
201
#### isIntrinsicErrorType
202
203
Test if a type is the `error` intrinsic type (occurs when TypeScript encounters resolution errors).
204
205
```typescript { .api }
206
function isIntrinsicErrorType(type: ts.Type): type is IntrinsicErrorType
207
```
208
209
#### isIntrinsicESSymbolType
210
211
Test if a type is the `symbol` intrinsic type.
212
213
```typescript { .api }
214
function isIntrinsicESSymbolType(type: ts.Type): type is IntrinsicESSymbolType
215
```
216
217
#### isIntrinsicNeverType
218
219
Test if a type is the `never` intrinsic type.
220
221
```typescript { .api }
222
function isIntrinsicNeverType(type: ts.Type): type is IntrinsicNeverType
223
```
224
225
#### isIntrinsicNonPrimitiveType
226
227
Test if a type is a non-primitive intrinsic type (e.g., `object`).
228
229
```typescript { .api }
230
function isIntrinsicNonPrimitiveType(type: ts.Type): type is IntrinsicNonPrimitiveType
231
```
232
233
#### isIntrinsicNullType
234
235
Test if a type is the `null` intrinsic type.
236
237
```typescript { .api }
238
function isIntrinsicNullType(type: ts.Type): type is IntrinsicNullType
239
```
240
241
#### isIntrinsicNumberType
242
243
Test if a type is the `number` intrinsic type.
244
245
```typescript { .api }
246
function isIntrinsicNumberType(type: ts.Type): type is IntrinsicNumberType
247
```
248
249
#### isIntrinsicStringType
250
251
Test if a type is the `string` intrinsic type.
252
253
```typescript { .api }
254
function isIntrinsicStringType(type: ts.Type): type is IntrinsicStringType
255
```
256
257
#### isIntrinsicUndefinedType
258
259
Test if a type is the `undefined` intrinsic type.
260
261
```typescript { .api }
262
function isIntrinsicUndefinedType(type: ts.Type): type is IntrinsicUndefinedType
263
```
264
265
#### isIntrinsicUnknownType
266
267
Test if a type is the `unknown` intrinsic type.
268
269
```typescript { .api }
270
function isIntrinsicUnknownType(type: ts.Type): type is IntrinsicUnknownType
271
```
272
273
#### isIntrinsicVoidType
274
275
Test if a type is the `void` intrinsic type.
276
277
```typescript { .api }
278
function isIntrinsicVoidType(type: ts.Type): type is IntrinsicVoidType
279
```
280
281
**Example:**
282
```typescript
283
declare const type: ts.Type;
284
285
if (isIntrinsicStringType(type)) {
286
// Type is the built-in string type
287
} else if (isIntrinsicNumberType(type)) {
288
// Type is the built-in number type
289
} else if (isIntrinsicErrorType(type)) {
290
// TypeScript encountered an error resolving this type
291
}
292
```
293
294
### Literal Type Guards
295
296
Functions that check for literal types (specific values like `"hello"` or `42`).
297
298
#### Type Definitions
299
300
```typescript { .api }
301
interface BooleanLiteralType extends FreshableIntrinsicType {
302
intrinsicName: "false" | "true";
303
}
304
305
interface FalseLiteralType extends BooleanLiteralType {
306
intrinsicName: "false";
307
}
308
309
interface TrueLiteralType extends BooleanLiteralType {
310
intrinsicName: "true";
311
}
312
313
interface UnknownLiteralType extends FreshableIntrinsicType {
314
value?: unknown;
315
}
316
```
317
318
#### isBigIntLiteralType
319
320
Test if a type is a bigint literal type (e.g., `123n`).
321
322
```typescript { .api }
323
function isBigIntLiteralType(type: ts.Type): type is ts.BigIntLiteralType
324
```
325
326
#### isBooleanLiteralType
327
328
Test if a type is a boolean literal type (`true` or `false`).
329
330
```typescript { .api }
331
function isBooleanLiteralType(type: ts.Type): type is BooleanLiteralType
332
```
333
334
#### isFalseLiteralType
335
336
Test if a type is the `false` literal type.
337
338
```typescript { .api }
339
function isFalseLiteralType(type: ts.Type): type is FalseLiteralType
340
```
341
342
#### isTrueLiteralType
343
344
Test if a type is the `true` literal type.
345
346
```typescript { .api }
347
function isTrueLiteralType(type: ts.Type): type is TrueLiteralType
348
```
349
350
#### isLiteralType
351
352
Test if a type is any kind of literal type.
353
354
```typescript { .api }
355
function isLiteralType(type: ts.Type): type is ts.LiteralType
356
```
357
358
#### isNumberLiteralType
359
360
Test if a type is a number literal type (e.g., `42`).
361
362
```typescript { .api }
363
function isNumberLiteralType(type: ts.Type): type is ts.NumberLiteralType
364
```
365
366
#### isStringLiteralType
367
368
Test if a type is a string literal type (e.g., `"hello"`).
369
370
```typescript { .api }
371
function isStringLiteralType(type: ts.Type): type is ts.StringLiteralType
372
```
373
374
#### isTemplateLiteralType
375
376
Test if a type is a template literal type (e.g., \`hello ${string}\`).
377
378
```typescript { .api }
379
function isTemplateLiteralType(type: ts.Type): type is ts.TemplateLiteralType
380
```
381
382
**Example:**
383
```typescript
384
declare const type: ts.Type;
385
386
if (isStringLiteralType(type)) {
387
console.log(`String literal: ${type.value}`);
388
} else if (isNumberLiteralType(type)) {
389
console.log(`Number literal: ${type.value}`);
390
} else if (isTrueLiteralType(type)) {
391
console.log('Boolean literal: true');
392
}
393
```
394
395
### Object Type Guards
396
397
Functions that check for object-related types.
398
399
#### isEvolvingArrayType
400
401
Test if a type is an evolving array type (used during type inference).
402
403
```typescript { .api }
404
function isEvolvingArrayType(type: ts.Type): type is ts.EvolvingArrayType
405
```
406
407
#### isTupleType
408
409
Test if a type is a tuple type.
410
411
```typescript { .api }
412
function isTupleType(type: ts.Type): type is ts.TupleType
413
```
414
415
#### isTypeReference
416
417
Test if a type is a type reference (generic type instantiation).
418
419
```typescript { .api }
420
function isTypeReference(type: ts.Type): type is ts.TypeReference
421
```
422
423
**Example:**
424
```typescript
425
declare const type: ts.Type;
426
427
if (isTupleType(type)) {
428
// Handle tuple type like [string, number]
429
} else if (isTypeReference(type)) {
430
// Handle generic type instantiation like Array<string>
431
}
432
```
433
434
### Single Type Guards
435
436
Functions that check for single, specific type categories.
437
438
#### isConditionalType
439
440
Test if a type is a conditional type (`T extends U ? X : Y`).
441
442
```typescript { .api }
443
function isConditionalType(type: ts.Type): type is ts.ConditionalType
444
```
445
446
#### isEnumType
447
448
Test if a type is an enum type.
449
450
```typescript { .api }
451
function isEnumType(type: ts.Type): type is ts.EnumType
452
```
453
454
#### isFreshableType
455
456
Test if a type is freshable (can have fresh literal types).
457
458
```typescript { .api }
459
function isFreshableType(type: ts.Type): type is ts.FreshableType
460
```
461
462
#### isIndexedAccessType
463
464
Test if a type is an indexed access type (`T[K]`).
465
466
```typescript { .api }
467
function isIndexedAccessType(type: ts.Type): type is ts.IndexedAccessType
468
```
469
470
#### isIndexType
471
472
Test if a type is an index type (`keyof T`).
473
474
```typescript { .api }
475
function isIndexType(type: ts.Type): type is ts.IndexType
476
```
477
478
#### isInstantiableType
479
480
Test if a type is instantiable (can be instantiated with type arguments).
481
482
```typescript { .api }
483
function isInstantiableType(type: ts.Type): type is ts.InstantiableType
484
```
485
486
#### isIntersectionType
487
488
Test if a type is an intersection type (`A & B`).
489
490
```typescript { .api }
491
function isIntersectionType(type: ts.Type): type is ts.IntersectionType
492
```
493
494
#### isObjectType
495
496
Test if a type is an object type.
497
498
```typescript { .api }
499
function isObjectType(type: ts.Type): type is ts.ObjectType
500
```
501
502
#### isStringMappingType
503
504
Test if a type is a string mapping type (like `Uppercase<T>`).
505
506
```typescript { .api }
507
function isStringMappingType(type: ts.Type): type is ts.StringMappingType
508
```
509
510
#### isSubstitutionType
511
512
Test if a type is a substitution type.
513
514
```typescript { .api }
515
function isSubstitutionType(type: ts.Type): type is ts.SubstitutionType
516
```
517
518
#### isTypeParameter
519
520
Test if a type is a type parameter. Note: This is intentionally not a type guard.
521
522
```typescript { .api }
523
function isTypeParameter(type: ts.Type): boolean
524
```
525
526
#### isTypeVariable
527
528
Test if a type is a type variable.
529
530
```typescript { .api }
531
function isTypeVariable(type: ts.Type): type is ts.TypeVariable
532
```
533
534
#### isUnionOrIntersectionType
535
536
Test if a type is either a union or intersection type.
537
538
```typescript { .api }
539
function isUnionOrIntersectionType(type: ts.Type): type is ts.UnionOrIntersectionType
540
```
541
542
#### isUnionType
543
544
Test if a type is a union type (`A | B`).
545
546
```typescript { .api }
547
function isUnionType(type: ts.Type): type is ts.UnionType
548
```
549
550
#### isUniqueESSymbolType
551
552
Test if a type is a unique ES symbol type.
553
554
```typescript { .api }
555
function isUniqueESSymbolType(type: ts.Type): type is ts.UniqueESSymbolType
556
```
557
558
### Compound Type Guards
559
560
Functions that check for types with multiple characteristics.
561
562
#### Type Definitions
563
564
```typescript { .api }
565
interface FreshableIntrinsicType extends ts.FreshableType, IntrinsicType {}
566
```
567
568
#### isFreshableIntrinsicType
569
570
Test if a type is both intrinsic and freshable.
571
572
```typescript { .api }
573
function isFreshableIntrinsicType(type: ts.Type): type is FreshableIntrinsicType
574
```
575
576
#### isTupleTypeReference
577
578
Test if a type is a tuple type reference (reference to a tuple type).
579
580
```typescript { .api }
581
function isTupleTypeReference(type: ts.Type): type is ts.TupleTypeReference
582
```
583
584
## Type Utilities
585
586
Helper functions for working with and manipulating types.
587
588
### intersectionTypeParts
589
590
Get the constituent types of an intersection type. If the type is not an intersection, returns an array containing only that type.
591
592
```typescript { .api }
593
function intersectionTypeParts(type: ts.Type): ts.Type[]
594
```
595
596
**Parameters:**
597
- `type: ts.Type` - The type to decompose
598
599
**Returns:** Array of constituent types
600
601
**Example:**
602
```typescript
603
declare const type: ts.Type;
604
605
for (const typePart of intersectionTypeParts(type)) {
606
// Process each part of the intersection (or the type itself)
607
}
608
```
609
610
### unionTypeParts
611
612
Get the constituent types of a union type. If the type is not a union, returns an array containing only that type.
613
614
```typescript { .api }
615
function unionTypeParts(type: ts.Type): ts.Type[]
616
```
617
618
**Parameters:**
619
- `type: ts.Type` - The type to decompose
620
621
**Returns:** Array of constituent types
622
623
**Example:**
624
```typescript
625
declare const type: ts.Type;
626
627
for (const typePart of unionTypeParts(type)) {
628
// Process each part of the union (or the type itself)
629
}
630
```
631
632
### typeParts
633
634
Get the constituent types of either a union or intersection type. Returns `type.types` for union/intersection types, or `[type]` for other types.
635
636
```typescript { .api }
637
function typeParts(type: ts.Type): ts.Type[]
638
```
639
640
**Parameters:**
641
- `type: ts.Type` - The type to decompose
642
643
**Returns:** Array of constituent types
644
645
### isFalsyType
646
647
Determine if a type is definitely falsy (but doesn't unwrap union types).
648
649
```typescript { .api }
650
function isFalsyType(type: ts.Type): boolean
651
```
652
653
**Parameters:**
654
- `type: ts.Type` - The type to check
655
656
**Returns:** `true` if the type is definitely falsy
657
658
**Example:**
659
```typescript
660
declare const type: ts.Type;
661
662
if (isFalsyType(type)) {
663
// Type is falsy (null, undefined, false, 0, "", etc.)
664
}
665
```
666
667
### isPropertyReadonlyInType
668
669
Determine whether writing to a specific property of a type is allowed.
670
671
```typescript { .api }
672
function isPropertyReadonlyInType(
673
type: ts.Type,
674
name: ts.__String,
675
typeChecker: ts.TypeChecker
676
): boolean
677
```
678
679
**Parameters:**
680
- `type: ts.Type` - The type containing the property
681
- `name: ts.__String` - The property name to check
682
- `typeChecker: ts.TypeChecker` - Type checker for analysis
683
684
**Returns:** `true` if the property is readonly
685
686
**Example:**
687
```typescript
688
declare const property: ts.Symbol;
689
declare const type: ts.Type;
690
declare const typeChecker: ts.TypeChecker;
691
692
if (isPropertyReadonlyInType(type, property.getEscapedName(), typeChecker)) {
693
// Property cannot be written to
694
}
695
```
696
697
### isThenableType
698
699
Determine if a type is thenable (has a `then` method) and can be used with `await`.
700
701
```typescript { .api }
702
function isThenableType(
703
typeChecker: ts.TypeChecker,
704
node: ts.Node,
705
type: ts.Type
706
): boolean
707
708
function isThenableType(
709
typeChecker: ts.TypeChecker,
710
node: ts.Expression,
711
type?: ts.Type
712
): boolean
713
```
714
715
**Parameters:**
716
- `typeChecker: ts.TypeChecker` - Type checker for analysis
717
- `node: ts.Node | ts.Expression` - The AST node for context
718
- `type?: ts.Type` - Optional type to check (inferred from node if not provided)
719
720
**Returns:** `true` if the type is thenable
721
722
**Example:**
723
```typescript
724
declare const node: ts.Node;
725
declare const type: ts.Type;
726
declare const typeChecker: ts.TypeChecker;
727
728
if (isThenableType(typeChecker, node, type)) {
729
// Type can be awaited
730
}
731
```
732
733
### symbolHasReadonlyDeclaration
734
735
Test if a symbol has any readonly declarations.
736
737
```typescript { .api }
738
function symbolHasReadonlyDeclaration(
739
symbol: ts.Symbol,
740
typeChecker: ts.TypeChecker
741
): boolean
742
```
743
744
**Parameters:**
745
- `symbol: ts.Symbol` - The symbol to check
746
- `typeChecker: ts.TypeChecker` - Type checker for analysis
747
748
**Returns:** `true` if the symbol has readonly declarations
749
750
**Example:**
751
```typescript
752
declare const symbol: ts.Symbol;
753
declare const typeChecker: ts.TypeChecker;
754
755
if (symbolHasReadonlyDeclaration(symbol, typeChecker)) {
756
// Symbol is declared as readonly
757
}
758
```
759
760
### typeIsLiteral
761
762
Test if a type is a literal type, with proper handling for bigint literals in older TypeScript versions.
763
764
```typescript { .api }
765
function typeIsLiteral(type: ts.Type): type is ts.LiteralType
766
```
767
768
**Parameters:**
769
- `type: ts.Type` - The type to check
770
771
**Returns:** `true` if the type is a literal type
772
773
**Note:** This function provides a compatibility layer for TypeScript versions before 5.0, where `type.isLiteral()` didn't correctly handle bigint literals.
774
775
**Example:**
776
```typescript
777
declare const type: ts.Type;
778
779
if (typeIsLiteral(type)) {
780
// Type is a literal (string, number, bigint, etc.)
781
}
782
```
783
784
## Usage Examples
785
786
### Analyzing Function Types
787
788
```typescript
789
function analyzeCallableType(type: ts.Type, typeChecker: ts.TypeChecker) {
790
const signatures = getCallSignaturesOfType(type);
791
792
if (signatures.length === 0) {
793
console.log('Type is not callable');
794
return;
795
}
796
797
console.log(`Type has ${signatures.length} call signature(s)`);
798
for (const signature of signatures) {
799
const params = signature.getParameters();
800
console.log(` ${params.length} parameter(s)`);
801
}
802
}
803
```
804
805
### Type Classification
806
807
```typescript
808
function classifyType(type: ts.Type): string {
809
if (isIntrinsicType(type)) {
810
return `Intrinsic: ${type.intrinsicName || 'unknown'}`;
811
}
812
813
if (isLiteralType(type)) {
814
if (isStringLiteralType(type)) {
815
return `String literal: "${type.value}"`;
816
} else if (isNumberLiteralType(type)) {
817
return `Number literal: ${type.value}`;
818
} else if (isBooleanLiteralType(type)) {
819
return `Boolean literal: ${type.intrinsicName}`;
820
}
821
return 'Literal type';
822
}
823
824
if (isUnionType(type)) {
825
const parts = unionTypeParts(type);
826
return `Union of ${parts.length} types`;
827
}
828
829
if (isIntersectionType(type)) {
830
const parts = intersectionTypeParts(type);
831
return `Intersection of ${parts.length} types`;
832
}
833
834
return 'Other type';
835
}
836
```
837
838
### Property Analysis
839
840
```typescript
841
function analyzeProperty(
842
type: ts.Type,
843
propertyName: string,
844
typeChecker: ts.TypeChecker
845
) {
846
const escapedName = propertyName as ts.__String;
847
const property = getPropertyOfType(type, escapedName);
848
849
if (!property) {
850
console.log(`Property '${propertyName}' not found`);
851
return;
852
}
853
854
const isReadonly = isPropertyReadonlyInType(type, escapedName, typeChecker);
855
console.log(`Property '${propertyName}' is ${isReadonly ? 'readonly' : 'mutable'}`);
856
857
if (symbolHasReadonlyDeclaration(property, typeChecker)) {
858
console.log('Property has readonly declaration');
859
}
860
}
861
```
862
863
The Type System Utilities provide the foundational tools needed for sophisticated TypeScript analysis, enabling developers to build tools that understand and work with TypeScript's rich type system in a reliable and comprehensive way.