0
# Advanced Mapped Types
1
2
Advanced mapped types provide sophisticated type transformations for object manipulation, key selection, property filtering, and complex type operations. These utilities enable powerful compile-time type transformations for building type-safe applications.
3
4
## Import
5
6
```typescript
7
import {
8
// Set operations
9
SetIntersection, SetDifference, SetComplement, SymmetricDifference,
10
11
// Object manipulation
12
Omit, Optional, Required, Diff, Subtract, Overwrite, Assign,
13
Intersection, PickByValue, PickByValueExact, OmitByValue, OmitByValueExact,
14
15
// Key selection
16
FunctionKeys, NonFunctionKeys, MutableKeys, WritableKeys, ReadonlyKeys,
17
RequiredKeys, OptionalKeys,
18
19
// Advanced utilities
20
NonUndefined, Unionize, PromiseType, Brand, Exact, ValuesType, UnionToIntersection,
21
Mutable, Writable,
22
23
// Deep transformations
24
DeepReadonly, DeepRequired, DeepNonNullable, DeepPartial
25
} from 'utility-types';
26
```
27
28
For CommonJS:
29
30
```javascript
31
const {
32
// Set operations
33
SetIntersection, SetDifference, SetComplement, SymmetricDifference,
34
35
// Object manipulation
36
Omit, Optional, Required, Diff, Subtract, Overwrite, Assign,
37
Intersection, PickByValue, PickByValueExact, OmitByValue, OmitByValueExact,
38
39
// Key selection
40
FunctionKeys, NonFunctionKeys, MutableKeys, WritableKeys, ReadonlyKeys,
41
RequiredKeys, OptionalKeys,
42
43
// Advanced utilities
44
NonUndefined, Unionize, PromiseType, Brand, Exact, ValuesType, UnionToIntersection,
45
Mutable, Writable,
46
47
// Deep transformations
48
DeepReadonly, DeepRequired, DeepNonNullable, DeepPartial
49
} = require('utility-types');
50
```
51
52
## Set Operations
53
54
### SetIntersection
55
56
Get the intersection of two union types (same as Extract).
57
58
```typescript { .api }
59
type SetIntersection<A, B> = A extends B ? A : never;
60
```
61
62
**Usage:**
63
64
```typescript
65
type Colors = 'red' | 'green' | 'blue';
66
type Warm = 'red' | 'orange' | 'yellow';
67
68
type WarmColors = SetIntersection<Colors, Warm>;
69
// Result: 'red'
70
71
type StringOrNumber = string | number;
72
type NumberOrBoolean = number | boolean;
73
74
type CommonType = SetIntersection<StringOrNumber, NumberOrBoolean>;
75
// Result: number
76
77
// With function types
78
type AsyncFunction = () => Promise<any>;
79
type SyncFunction = () => any;
80
81
type AsyncFromMixed = SetIntersection<AsyncFunction | SyncFunction, Function>;
82
// Result: () => Promise<any> | () => any
83
```
84
85
### SetDifference
86
87
Get the difference between two union types (same as Exclude).
88
89
```typescript { .api }
90
type SetDifference<A, B> = A extends B ? never : A;
91
```
92
93
**Usage:**
94
95
```typescript
96
type AllTypes = string | number | boolean | null;
97
type TruthyTypes = string | number | boolean;
98
99
type FalsyTypes = SetDifference<AllTypes, TruthyTypes>;
100
// Result: null
101
102
type Events = 'click' | 'focus' | 'blur' | 'keydown';
103
type MouseEvents = 'click' | 'mousedown' | 'mouseup';
104
105
type NonMouseEvents = SetDifference<Events, MouseEvents>;
106
// Result: 'focus' | 'blur' | 'keydown'
107
```
108
109
### SetComplement
110
111
Get the complement of a subset within a superset.
112
113
```typescript { .api }
114
type SetComplement<A, A1 extends A> = SetDifference<A, A1>;
115
```
116
117
**Usage:**
118
119
```typescript
120
type AllPermissions = 'read' | 'write' | 'delete' | 'admin';
121
type UserPermissions = 'read' | 'write';
122
123
type MissingPermissions = SetComplement<AllPermissions, UserPermissions>;
124
// Result: 'delete' | 'admin'
125
126
// Useful for ensuring exhaustive handling
127
function handleRemainingCases(permission: SetComplement<AllPermissions, 'read' | 'write'>) {
128
// permission is typed as 'delete' | 'admin'
129
switch (permission) {
130
case 'delete':
131
return handleDelete();
132
case 'admin':
133
return handleAdmin();
134
}
135
}
136
```
137
138
### SymmetricDifference
139
140
Get the symmetric difference of two union types.
141
142
```typescript { .api }
143
type SymmetricDifference<A, B> = SetDifference<A | B, A & B>;
144
```
145
146
**Usage:**
147
148
```typescript
149
type SetA = 'a' | 'b' | 'c';
150
type SetB = 'b' | 'c' | 'd';
151
152
type OnlyInAOrB = SymmetricDifference<SetA, SetB>;
153
// Result: 'a' | 'd'
154
155
type Frontend = 'react' | 'vue' | 'angular';
156
type Backend = 'node' | 'django' | 'rails' | 'vue';
157
158
type ExclusiveTechs = SymmetricDifference<Frontend, Backend>;
159
// Result: 'react' | 'angular' | 'node' | 'django' | 'rails'
160
```
161
162
## Object Manipulation
163
164
### Omit
165
166
Remove specified properties from an object type.
167
168
```typescript { .api }
169
type Omit<T, K extends keyof any> = Pick<T, SetDifference<keyof T, K>>;
170
```
171
172
**Usage:**
173
174
```typescript
175
interface User {
176
id: number;
177
name: string;
178
email: string;
179
password: string;
180
createdAt: Date;
181
}
182
183
type PublicUser = Omit<User, 'password'>;
184
// Result: { id: number; name: string; email: string; createdAt: Date; }
185
186
type UserSummary = Omit<User, 'password' | 'createdAt'>;
187
// Result: { id: number; name: string; email: string; }
188
189
// Works with union types
190
type ApiResponse =
191
| { type: 'success'; data: any; timestamp: number }
192
| { type: 'error'; message: string; timestamp: number };
193
194
type ResponseWithoutTimestamp = Omit<ApiResponse, 'timestamp'>;
195
// Result: { type: 'success'; data: any } | { type: 'error'; message: string }
196
```
197
198
### Optional
199
200
Make specified properties optional.
201
202
```typescript { .api }
203
type Optional<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
204
```
205
206
**Usage:**
207
208
```typescript
209
interface CreateUserRequest {
210
name: string;
211
email: string;
212
age: number;
213
avatar: string;
214
}
215
216
type CreateUserWithDefaults = Optional<CreateUserRequest, 'age' | 'avatar'>;
217
// Result: { name: string; email: string; age?: number; avatar?: string; }
218
219
// Make all properties optional
220
type PartialUser = Optional<CreateUserRequest>;
221
// Result: { name?: string; email?: string; age?: number; avatar?: string; }
222
223
function createUser(data: CreateUserWithDefaults) {
224
const user = {
225
...data,
226
age: data.age ?? 18,
227
avatar: data.avatar ?? '/default-avatar.png'
228
};
229
return user;
230
}
231
```
232
233
### Required (AugmentedRequired)
234
235
Make specified properties required.
236
237
```typescript { .api }
238
type Required<T extends object, K extends keyof T = keyof T> = Omit<T, K> & Required<Pick<T, K>>;
239
```
240
241
**Usage:**
242
243
```typescript
244
interface PartialConfig {
245
host?: string;
246
port?: number;
247
timeout?: number;
248
retries?: number;
249
}
250
251
type RequiredConfig = Required<PartialConfig, 'host' | 'port'>;
252
// Result: { host: string; port: number; timeout?: number; retries?: number; }
253
254
// Make all properties required
255
type FullConfig = Required<PartialConfig>;
256
// Result: { host: string; port: number; timeout: number; retries: number; }
257
258
function connect(config: RequiredConfig) {
259
// host and port are guaranteed to exist
260
console.log(`Connecting to ${config.host}:${config.port}`);
261
}
262
```
263
264
### Diff
265
266
Remove properties that exist in the second type from the first type.
267
268
```typescript { .api }
269
type Diff<T extends object, U extends object> = Pick<T, SetDifference<keyof T, keyof U>>;
270
```
271
272
**Usage:**
273
274
```typescript
275
interface ExtendedUser {
276
id: number;
277
name: string;
278
email: string;
279
password: string;
280
isAdmin: boolean;
281
lastLogin: Date;
282
}
283
284
interface BasicUser {
285
id: number;
286
name: string;
287
email: string;
288
}
289
290
type UserExtensions = Diff<ExtendedUser, BasicUser>;
291
// Result: { password: string; isAdmin: boolean; lastLogin: Date; }
292
293
// Common pattern: finding unique properties
294
interface ComponentProps {
295
className?: string;
296
style?: React.CSSProperties;
297
children?: React.ReactNode;
298
onClick: () => void;
299
disabled: boolean;
300
}
301
302
interface HTMLButtonProps {
303
className?: string;
304
style?: React.CSSProperties;
305
children?: React.ReactNode;
306
disabled?: boolean;
307
}
308
309
type CustomProps = Diff<ComponentProps, HTMLButtonProps>;
310
// Result: { onClick: () => void; disabled: boolean; }
311
```
312
313
### PickByValue
314
315
Pick properties from an object type based on their value types.
316
317
```typescript { .api }
318
type PickByValue<T, ValueType> = Pick<T, {
319
[Key in keyof T]: T[Key] extends ValueType ? Key : never;
320
}[keyof T]>;
321
```
322
323
**Usage:**
324
325
```typescript
326
interface MixedData {
327
id: number;
328
name: string;
329
count: number;
330
active: boolean;
331
tags: string[];
332
metadata: Record<string, any>;
333
}
334
335
type StringFields = PickByValue<MixedData, string>;
336
// Result: { name: string; }
337
338
type NumberFields = PickByValue<MixedData, number>;
339
// Result: { id: number; count: number; }
340
341
type ArrayFields = PickByValue<MixedData, any[]>;
342
// Result: { tags: string[]; }
343
344
// Useful for creating type-safe field selectors
345
function getStringFields<T>(obj: T): PickByValue<T, string> {
346
const result = {} as any;
347
for (const [key, value] of Object.entries(obj)) {
348
if (typeof value === 'string') {
349
result[key] = value;
350
}
351
}
352
return result;
353
}
354
```
355
356
### OmitByValue
357
358
Remove properties from an object type based on their value types.
359
360
```typescript { .api }
361
type OmitByValue<T, ValueType> = Pick<T, {
362
[Key in keyof T]: T[Key] extends ValueType ? never : Key;
363
}[keyof T]>;
364
```
365
366
**Usage:**
367
368
```typescript
369
interface UserData {
370
id: number;
371
name: string;
372
email: string;
373
isActive: boolean;
374
loginCount: number;
375
settings: Record<string, any>;
376
}
377
378
type NonStringFields = OmitByValue<UserData, string>;
379
// Result: { id: number; isActive: boolean; loginCount: number; settings: Record<string, any>; }
380
381
type NonNumberFields = OmitByValue<UserData, number>;
382
// Result: { name: string; email: string; isActive: boolean; settings: Record<string, any>; }
383
384
// Remove function properties
385
interface EventHandler {
386
name: string;
387
type: string;
388
handler: () => void;
389
cleanup: () => void;
390
priority: number;
391
}
392
393
type EventData = OmitByValue<EventHandler, Function>;
394
// Result: { name: string; type: string; priority: number; }
395
```
396
397
## Key Selection
398
399
### FunctionKeys
400
401
Get keys of properties that are functions.
402
403
```typescript { .api }
404
type FunctionKeys<T extends object> = {
405
[K in keyof T]-?: NonUndefined<T[K]> extends Function ? K : never;
406
}[keyof T];
407
```
408
409
**Usage:**
410
411
```typescript
412
class UserService {
413
name: string = 'UserService';
414
version: number = 1;
415
416
getUser(id: number): User { return {} as User; }
417
createUser(data: CreateUserData): User { return {} as User; }
418
deleteUser(id: number): void {}
419
}
420
421
type UserServiceMethods = FunctionKeys<UserService>;
422
// Result: 'getUser' | 'createUser' | 'deleteUser'
423
424
// Create method-only interface
425
type UserServiceInterface = Pick<UserService, FunctionKeys<UserService>>;
426
// Result: {
427
// getUser(id: number): User;
428
// createUser(data: CreateUserData): User;
429
// deleteUser(id: number): void;
430
// }
431
```
432
433
### NonFunctionKeys
434
435
Get keys of properties that are not functions.
436
437
```typescript { .api }
438
type NonFunctionKeys<T extends object> = {
439
[K in keyof T]-?: NonUndefined<T[K]> extends Function ? never : K;
440
}[keyof T];
441
```
442
443
**Usage:**
444
445
```typescript
446
class DatabaseConnection {
447
host: string = 'localhost';
448
port: number = 5432;
449
isConnected: boolean = false;
450
451
connect(): Promise<void> { return Promise.resolve(); }
452
disconnect(): void {}
453
query(sql: string): Promise<any[]> { return Promise.resolve([]); }
454
}
455
456
type ConnectionData = NonFunctionKeys<DatabaseConnection>;
457
// Result: 'host' | 'port' | 'isConnected'
458
459
type ConnectionState = Pick<DatabaseConnection, NonFunctionKeys<DatabaseConnection>>;
460
// Result: { host: string; port: number; isConnected: boolean; }
461
```
462
463
### RequiredKeys
464
465
Get keys of properties that are required (not optional).
466
467
```typescript { .api }
468
type RequiredKeys<T> = {
469
[K in keyof T]: {} extends Pick<T, K> ? never : K;
470
}[keyof T];
471
```
472
473
**Usage:**
474
475
```typescript
476
interface UserProfile {
477
id: number;
478
name: string;
479
email?: string;
480
avatar?: string;
481
bio?: string;
482
}
483
484
type RequiredUserFields = RequiredKeys<UserProfile>;
485
// Result: 'id' | 'name'
486
487
type MinimalUser = Pick<UserProfile, RequiredKeys<UserProfile>>;
488
// Result: { id: number; name: string; }
489
490
// Useful for validation
491
function validateRequiredFields<T>(obj: Partial<T>): obj is Pick<T, RequiredKeys<T>> {
492
// Implementation would check if all required fields are present
493
return true; // Simplified
494
}
495
```
496
497
### OptionalKeys
498
499
Get keys of properties that are optional.
500
501
```typescript { .api }
502
type OptionalKeys<T> = {
503
[K in keyof T]: {} extends Pick<T, K> ? K : never;
504
}[keyof T];
505
```
506
507
**Usage:**
508
509
```typescript
510
interface CreatePostRequest {
511
title: string;
512
content: string;
513
tags?: string[];
514
publishedAt?: Date;
515
featuredImage?: string;
516
}
517
518
type OptionalPostFields = OptionalKeys<CreatePostRequest>;
519
// Result: 'tags' | 'publishedAt' | 'featuredImage'
520
521
type PostDefaults = Pick<CreatePostRequest, OptionalKeys<CreatePostRequest>>;
522
// Result: { tags?: string[]; publishedAt?: Date; featuredImage?: string; }
523
524
function applyDefaults(request: CreatePostRequest): Required<CreatePostRequest> {
525
return {
526
...request,
527
tags: request.tags ?? [],
528
publishedAt: request.publishedAt ?? new Date(),
529
featuredImage: request.featuredImage ?? '/default-image.jpg'
530
};
531
}
532
```
533
534
## Advanced Utilities
535
536
### UnionToIntersection
537
538
Convert a union type to an intersection type.
539
540
```typescript { .api }
541
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
542
```
543
544
**Usage:**
545
546
```typescript
547
type A = { a: string };
548
type B = { b: number };
549
type C = { c: boolean };
550
551
type Union = A | B | C;
552
type Intersection = UnionToIntersection<Union>;
553
// Result: { a: string } & { b: number } & { c: boolean }
554
// Which is equivalent to: { a: string; b: number; c: boolean }
555
556
// Useful for combining multiple object types
557
interface BaseConfig { timeout: number; }
558
interface AuthConfig { apiKey: string; }
559
interface CacheConfig { cacheSize: number; }
560
561
type ConfigUnion = BaseConfig | AuthConfig | CacheConfig;
562
type FullConfig = UnionToIntersection<ConfigUnion>;
563
// Result: { timeout: number; apiKey: string; cacheSize: number; }
564
```
565
566
### Brand
567
568
Create a branded (nominal) type to distinguish values of the same underlying type.
569
570
```typescript { .api }
571
type Brand<T, U> = T & { __brand: U };
572
```
573
574
**Usage:**
575
576
```typescript
577
type UserId = Brand<number, 'UserId'>;
578
type ProductId = Brand<number, 'ProductId'>;
579
580
function getUser(id: UserId): User {
581
// Implementation
582
return {} as User;
583
}
584
585
function getProduct(id: ProductId): Product {
586
// Implementation
587
return {} as Product;
588
}
589
590
declare const userId: UserId;
591
declare const productId: ProductId;
592
593
getUser(userId); // OK
594
getProduct(productId); // OK
595
596
// getUser(productId); // Error: ProductId is not assignable to UserId
597
// getProduct(userId); // Error: UserId is not assignable to ProductId
598
599
// Creating branded values
600
function createUserId(id: number): UserId {
601
return id as UserId;
602
}
603
604
function createProductId(id: number): ProductId {
605
return id as ProductId;
606
}
607
608
const safeUserId = createUserId(123);
609
const safeProductId = createProductId(456);
610
```
611
612
### Exact
613
614
Create a branded object type for exact type matching.
615
616
```typescript { .api }
617
type Exact<A extends object> = A & { __brand: keyof A };
618
```
619
620
**Usage:**
621
622
```typescript
623
interface User {
624
id: number;
625
name: string;
626
}
627
628
type ExactUser = Exact<User>;
629
630
// Useful for ensuring exact object shapes
631
function processExactUser(user: ExactUser): void {
632
// Implementation
633
}
634
635
// This creates an exact match requirement
636
const user: ExactUser = { id: 1, name: 'Alice' } as ExactUser;
637
processExactUser(user);
638
639
// Note: This is primarily for advanced type-level programming
640
// Most applications should use regular TypeScript structural typing
641
```
642
643
### PromiseType
644
645
Extract the resolved type from a Promise type.
646
647
```typescript { .api }
648
type PromiseType<T extends Promise<any>> = T extends Promise<infer U> ? U : never;
649
```
650
651
**Usage:**
652
653
```typescript
654
async function fetchUser(id: number): Promise<{ name: string; email: string }> {
655
// Implementation
656
return { name: 'User', email: 'user@example.com' };
657
}
658
659
type UserType = PromiseType<ReturnType<typeof fetchUser>>;
660
// Result: { name: string; email: string }
661
662
// With generic promises
663
type StringPromise = Promise<string>;
664
type NumberArrayPromise = Promise<number[]>;
665
666
type StringType = PromiseType<StringPromise>; // Result: string
667
type NumberArrayType = PromiseType<NumberArrayPromise>; // Result: number[]
668
669
// Useful for working with async function types
670
function handleAsyncResult<T extends Promise<any>>(
671
promise: T,
672
callback: (result: PromiseType<T>) => void
673
) {
674
promise.then(callback);
675
}
676
```
677
678
### ValuesType
679
680
Get the union type of all values in an array, object, or array-like structure.
681
682
```typescript { .api }
683
type ValuesType<T extends ReadonlyArray<any> | ArrayLike<any> | Record<any, any>> =
684
T extends ReadonlyArray<any>
685
? T[number]
686
: T extends ArrayLike<any>
687
? T[number]
688
: T extends object
689
? T[keyof T]
690
: never;
691
```
692
693
**Usage:**
694
695
```typescript
696
// With arrays
697
const colors = ['red', 'green', 'blue'] as const;
698
type Color = ValuesType<typeof colors>;
699
// Result: 'red' | 'green' | 'blue'
700
701
// With objects
702
const statusCodes = {
703
OK: 200,
704
NOT_FOUND: 404,
705
SERVER_ERROR: 500
706
} as const;
707
708
type StatusCode = ValuesType<typeof statusCodes>;
709
// Result: 200 | 404 | 500
710
711
// With tuples
712
type UserTuple = [string, number, boolean];
713
type TupleValues = ValuesType<UserTuple>;
714
// Result: string | number | boolean
715
716
// Practical example
717
const eventTypes = {
718
CLICK: 'click',
719
FOCUS: 'focus',
720
BLUR: 'blur'
721
} as const;
722
723
type EventType = ValuesType<typeof eventTypes>;
724
725
function handleEvent(type: EventType) {
726
// type is 'click' | 'focus' | 'blur'
727
switch (type) {
728
case 'click':
729
// Handle click
730
break;
731
case 'focus':
732
// Handle focus
733
break;
734
case 'blur':
735
// Handle blur
736
break;
737
}
738
}
739
```
740
741
## Deep Transformations
742
743
### DeepReadonly
744
745
Recursively make all properties readonly, including nested objects and arrays.
746
747
```typescript { .api }
748
type DeepReadonly<T> = T extends ((...args: any[]) => any) | Primitive
749
? T
750
: T extends _DeepReadonlyArray<infer U>
751
? _DeepReadonlyArray<U>
752
: T extends _DeepReadonlyObject<infer V>
753
? _DeepReadonlyObject<V>
754
: T;
755
756
interface _DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
757
type _DeepReadonlyObject<T> = { readonly [P in keyof T]: DeepReadonly<T[P]> };
758
```
759
760
**Usage:**
761
762
```typescript
763
interface NestedConfig {
764
server: {
765
host: string;
766
port: number;
767
ssl: {
768
enabled: boolean;
769
cert: string;
770
key: string;
771
};
772
};
773
database: {
774
hosts: string[];
775
credentials: {
776
username: string;
777
password: string;
778
};
779
};
780
}
781
782
type ReadonlyConfig = DeepReadonly<NestedConfig>;
783
// All properties at every level become readonly:
784
// {
785
// readonly server: {
786
// readonly host: string;
787
// readonly port: number;
788
// readonly ssl: {
789
// readonly enabled: boolean;
790
// readonly cert: string;
791
// readonly key: string;
792
// };
793
// };
794
// readonly database: {
795
// readonly hosts: readonly string[];
796
// readonly credentials: {
797
// readonly username: string;
798
// readonly password: string;
799
// };
800
// };
801
// }
802
803
function processConfig(config: ReadonlyConfig) {
804
// config.server.host = 'new-host'; // Error: Cannot assign to readonly property
805
// config.database.hosts.push('new-host'); // Error: hosts is readonly array
806
console.log(config.server.host); // OK: reading is allowed
807
}
808
```
809
810
### DeepRequired
811
812
Recursively make all properties required, including nested objects and arrays.
813
814
```typescript { .api }
815
type DeepRequired<T> = T extends (...args: any[]) => any
816
? T
817
: T extends any[]
818
? _DeepRequiredArray<T[number]>
819
: T extends object
820
? _DeepRequiredObject<T>
821
: T;
822
823
interface _DeepRequiredArray<T> extends Array<DeepRequired<NonUndefined<T>>> {}
824
type _DeepRequiredObject<T> = { [P in keyof T]-?: DeepRequired<NonUndefined<T[P]>> };
825
```
826
827
**Usage:**
828
829
```typescript
830
interface PartialUser {
831
id?: number;
832
profile?: {
833
name?: string;
834
email?: string;
835
settings?: {
836
theme?: 'light' | 'dark';
837
notifications?: boolean;
838
};
839
};
840
posts?: Array<{
841
title?: string;
842
content?: string;
843
}>;
844
}
845
846
type CompleteUser = DeepRequired<PartialUser>;
847
// All properties at every level become required:
848
// {
849
// id: number;
850
// profile: {
851
// name: string;
852
// email: string;
853
// settings: {
854
// theme: 'light' | 'dark';
855
// notifications: boolean;
856
// };
857
// };
858
// posts: Array<{
859
// title: string;
860
// content: string;
861
// }>;
862
// }
863
864
function validateCompleteUser(user: unknown): user is CompleteUser {
865
// Validation logic that ensures all nested properties exist
866
return true; // Simplified
867
}
868
```
869
870
### DeepPartial
871
872
Recursively make all properties optional, including nested objects and arrays.
873
874
```typescript { .api }
875
type DeepPartial<T> = { [P in keyof T]?: _DeepPartial<T[P]> };
876
877
type _DeepPartial<T> = T extends Function
878
? T
879
: T extends Array<infer U>
880
? _DeepPartialArray<U>
881
: T extends object
882
? DeepPartial<T>
883
: T | undefined;
884
885
interface _DeepPartialArray<T> extends Array<_DeepPartial<T>> {}
886
```
887
888
**Usage:**
889
890
```typescript
891
interface StrictUser {
892
id: number;
893
profile: {
894
name: string;
895
email: string;
896
address: {
897
street: string;
898
city: string;
899
country: string;
900
};
901
};
902
preferences: {
903
theme: 'light' | 'dark';
904
language: string;
905
notifications: {
906
email: boolean;
907
push: boolean;
908
};
909
};
910
}
911
912
type FlexibleUserUpdate = DeepPartial<StrictUser>;
913
// All properties at every level become optional:
914
// {
915
// id?: number;
916
// profile?: {
917
// name?: string;
918
// email?: string;
919
// address?: {
920
// street?: string;
921
// city?: string;
922
// country?: string;
923
// };
924
// };
925
// preferences?: {
926
// theme?: 'light' | 'dark';
927
// language?: string;
928
// notifications?: {
929
// email?: boolean;
930
// push?: boolean;
931
// };
932
// };
933
// }
934
935
function updateUser(id: number, updates: FlexibleUserUpdate) {
936
// Any combination of nested properties can be provided
937
}
938
939
// Usage examples:
940
updateUser(1, { profile: { name: 'Alice' } });
941
updateUser(2, { preferences: { notifications: { email: false } } });
942
updateUser(3, { profile: { address: { city: 'New York' } } });
943
```
944
945
### DeepNonNullable
946
947
Recursively remove null and undefined from all properties, including nested structures.
948
949
```typescript { .api }
950
type DeepNonNullable<T> = T extends (...args: any[]) => any
951
? T
952
: T extends any[]
953
? _DeepNonNullableArray<T[number]>
954
: T extends object
955
? _DeepNonNullableObject<T>
956
: T;
957
958
interface _DeepNonNullableArray<T> extends Array<DeepNonNullable<NonNullable<T>>> {}
959
type _DeepNonNullableObject<T> = { [P in keyof T]-?: DeepNonNullable<NonNullable<T[P]>> };
960
```
961
962
**Usage:**
963
964
```typescript
965
interface NullableData {
966
id: number | null;
967
user: {
968
name: string | null;
969
email?: string | null;
970
profile: {
971
avatar: string | null | undefined;
972
bio: string | null;
973
} | null;
974
} | null;
975
tags: Array<string | null> | null;
976
}
977
978
type CleanData = DeepNonNullable<NullableData>;
979
// All null and undefined are removed at every level:
980
// {
981
// id: number;
982
// user: {
983
// name: string;
984
// email: string;
985
// profile: {
986
// avatar: string;
987
// bio: string;
988
// };
989
// };
990
// tags: Array<string>;
991
// }
992
993
function processCleanData(data: CleanData) {
994
// All properties are guaranteed to be non-null/non-undefined
995
console.log(data.user.name.toUpperCase()); // No null checks needed
996
console.log(data.user.profile.avatar.length); // Safe to access
997
data.tags.forEach(tag => console.log(tag.trim())); // No null in array
998
}
999
```