@@ -12,9 +12,38 @@ function isEmptyArray(arr: unknown) {
12
12
13
13
interface RemovalOptions {
14
14
preserveEmptyArray ?: boolean ;
15
+ preserveNullishArrays ?: boolean ;
15
16
removeAllFalsy ?: boolean ;
16
17
}
17
18
19
+ // Remove objects that has undefined value or recursively contain undefined values
20
+ // biome-ignore lint/suspicious/noExplicitAny: This method does its own type assertions.
21
+ function removeUndefined ( obj : any ) : any {
22
+ if ( obj === undefined ) {
23
+ return undefined ;
24
+ }
25
+ // Preserve null
26
+ if ( obj === null ) {
27
+ return null ;
28
+ }
29
+ // Remove undefined in arrays
30
+ if ( Array . isArray ( obj ) ) {
31
+ return obj . map ( removeUndefined ) . filter ( item => item !== undefined ) ;
32
+ }
33
+ if ( typeof obj === 'object' ) {
34
+ // biome-ignore lint/suspicious/noExplicitAny: We're just passing around the object values
35
+ const cleaned : Record < string , any > = { } ;
36
+ Object . entries ( obj ) . forEach ( ( [ key , value ] ) => {
37
+ const cleanedValue = removeUndefined ( value ) ;
38
+ if ( cleanedValue !== undefined ) {
39
+ cleaned [ key ] = cleanedValue ;
40
+ }
41
+ } ) ;
42
+ return cleaned ;
43
+ }
44
+ return obj ;
45
+ }
46
+
18
47
// Modified from here: https://stackoverflow.com/a/43781499
19
48
// biome-ignore lint/suspicious/noExplicitAny: This method does its own type assertions.
20
49
function stripEmptyObjects ( obj : any , options : RemovalOptions = { } ) {
@@ -69,8 +98,8 @@ function stripEmptyObjects(obj: any, options: RemovalOptions = {}) {
69
98
} else {
70
99
cleanObj [ idx ] = value ;
71
100
}
72
- } else if ( value === null ) {
73
- // Null entries within an array should be removed.
101
+ } else if ( value === null && ( options . removeAllFalsy || ! options . preserveNullishArrays ) ) {
102
+ // Null entries within an array should be removed by default, unless explicitly preserved
74
103
delete cleanObj [ idx ] ;
75
104
}
76
105
} ) ;
@@ -85,12 +114,12 @@ export default function removeUndefinedObjects<T>(obj?: T, options?: RemovalOpti
85
114
return undefined ;
86
115
}
87
116
88
- // JSON.stringify removes undefined values. Though `[undefined]` will be converted with this to
89
- // `[null]`, we'll clean that up next.
90
- // eslint-disable-next-line try-catch-failsafe/json-parse
91
- let withoutUndefined = JSON . parse ( JSON . stringify ( obj ) ) ;
117
+ // If array nulls are preserved, use the custom removeUndefined function so that
118
+ // undefined values in arrays aren't converted to nulls, which stringify does
119
+ // If we're not preserving array nulls (default behavior), it doesn't matter that the undefined array values are converted to nulls
120
+ let withoutUndefined = options ?. preserveNullishArrays ? removeUndefined ( obj ) : JSON . parse ( JSON . stringify ( obj ) ) ;
92
121
93
- // Then we recursively remove all empty objects and nullish arrays.
122
+ // Then we recursively remove all empty objects and nullish arrays
94
123
withoutUndefined = stripEmptyObjects ( withoutUndefined , options ) ;
95
124
96
125
// If the only thing that's leftover is an empty object or empty array then return nothing.
0 commit comments