@@ -13,43 +13,69 @@ type Flattened<T> = T extends Array<infer U>
1313 ? ExcludeFunctionsAndPrivate < T >
1414 : T
1515
16- function flatten < T > ( obj : T ) : Flattened < T > {
17- // Handle arrays
18- if ( Array . isArray ( obj ) ) {
19- return obj . map ( ( item ) => flatten ( item ) ) as Flattened < T >
20- }
21-
16+ function flatten < T > ( obj : T , ...props : Record < string , boolean | string > [ ] ) : Flattened < T > {
2217 // Handle null or non-objects
2318 if ( ! obj || typeof obj !== 'object' ) {
2419 return obj as Flattened < T >
2520 }
2621
27- // Handle Date objects
28- if ( obj instanceof Date ) {
29- return obj . toISOString ( ) as Flattened < T >
22+ // Handle arrays
23+ if ( Array . isArray ( obj ) ) {
24+ return obj . map ( ( item ) => flatten ( item ) ) as Flattened < T >
3025 }
3126
32- const result : Record < string , unknown > = { }
27+ // Get all non-private properties (exclude keys starting with '_')
28+ const objProps = Object . keys ( obj )
29+ . filter ( ( key ) => ! key . startsWith ( '_' ) )
30+ . map ( ( key ) => ( { [ key ] : true } ) )
3331
34- for ( const key in obj ) {
35- // biome-ignore lint/suspicious/noExplicitAny: Safe
36- if ( key . startsWith ( '_' ) || typeof ( obj as any ) [ key ] === 'function' ) {
37- continue
38- }
32+ // Merge provided props with object props safely
33+ const mergedProps : Record < string , boolean | string > = Object . assign (
34+ { } ,
35+ ...objProps , // Spread array of objects
36+ ...props , // Spread additional props
37+ )
38+
39+ const out : Record < string , unknown > = { }
40+
41+ for ( const [ prop , newProp ] of Object . entries ( mergedProps ) ) {
42+ if ( ! newProp ) continue
43+ const outputKey = newProp === true ? prop : newProp
3944
4045 // biome-ignore lint/suspicious/noExplicitAny: Safe
41- const value = ( obj as any ) [ key ]
42-
43- if ( value && typeof value === 'object' && 'toJSON' in value ) {
44- result [ key ] = flatten ( value )
45- } else if ( Array . isArray ( value ) ) {
46- result [ key ] = flatten ( value )
47- } else {
48- result [ key ] = value
46+ const element = ( obj as any ) [ prop ]
47+ const elemIsObj = element && typeof element === 'object'
48+ // biome-ignore lint/suspicious/noShadowRestrictedNames: Safe
49+ const valueOf = elemIsObj && typeof element . valueOf === 'function' ? element . valueOf ( ) : null
50+ const hasToJSON = elemIsObj && typeof element . toJSON === 'function'
51+
52+ // Handle Date objects explicitly
53+ if ( element instanceof Date ) {
54+ out [ outputKey ] = element . toISOString ( )
55+ }
56+ // If it's an array, process each element
57+ else if ( Array . isArray ( element ) ) {
58+ out [ outputKey ] = element . map ( ( elm ) => ( hasToJSON ? elm . toJSON ( ) : flatten ( elm ) ) )
59+ }
60+ // If it has a toJSON method, use its result
61+ else if ( hasToJSON ) {
62+ out [ outputKey ] = element . toJSON ( )
63+ }
64+ // If it's an object with a primitive valueOf, use that
65+ else if ( elemIsObj && valueOf && typeof valueOf !== 'object' ) {
66+ out [ outputKey ] = valueOf
67+ }
68+ // If it's a non-primitive object, flatten it
69+ else if ( elemIsObj ) {
70+ out [ outputKey ] = flatten ( element )
71+ }
72+ // Otherwise, use the element as-is (primitives)
73+ else {
74+ out [ outputKey ] = element
4975 }
5076 }
5177
52- return result as Flattened < T >
78+ return out as Flattened < T >
5379}
5480
5581export { flatten , type Flattened }
0 commit comments