@revolugo/common 7.13.1-alpha.1 → 7.13.1-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/utils/case-transformer-core.ts +72 -0
- package/src/utils/case-transformer.test.ts +73 -0
- package/src/utils/case-transformers.ts +11 -108
- package/src/utils/index.ts +1 -1
- package/src/utils/keys-case-transformer.ts +74 -51
- package/src/utils/pick-by.ts +37 -0
- package/src/utils/sum-by.ts +5 -2
package/package.json
CHANGED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import {
|
|
2
|
+
camelCase,
|
|
3
|
+
capitalCase,
|
|
4
|
+
kebabCase,
|
|
5
|
+
pascalCase,
|
|
6
|
+
snakeCase,
|
|
7
|
+
} from 'change-case'
|
|
8
|
+
import slugify from 'slugify'
|
|
9
|
+
|
|
10
|
+
function slugCase(input: string): string {
|
|
11
|
+
return slugify(kebabCase(input), {
|
|
12
|
+
lower: true,
|
|
13
|
+
strict: true,
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention -- public API constant object
|
|
18
|
+
export const CaseTransformer = {
|
|
19
|
+
Camel: 'camelCase',
|
|
20
|
+
Capital: 'capitalCase',
|
|
21
|
+
Param: 'paramCase',
|
|
22
|
+
Pascal: 'pascalCase',
|
|
23
|
+
Slug: 'slugCase',
|
|
24
|
+
Snake: 'snakeCase',
|
|
25
|
+
} as const
|
|
26
|
+
|
|
27
|
+
export type CaseTransformer =
|
|
28
|
+
(typeof CaseTransformer)[keyof typeof CaseTransformer]
|
|
29
|
+
|
|
30
|
+
export const CASE_TRANSFORMERS_MAPPING = {
|
|
31
|
+
[CaseTransformer.Camel]: camelCase,
|
|
32
|
+
[CaseTransformer.Capital]: capitalCase,
|
|
33
|
+
[CaseTransformer.Param]: kebabCase,
|
|
34
|
+
[CaseTransformer.Pascal]: pascalCase,
|
|
35
|
+
[CaseTransformer.Slug]: slugCase,
|
|
36
|
+
[CaseTransformer.Snake]: snakeCase,
|
|
37
|
+
} as const
|
|
38
|
+
|
|
39
|
+
export function changeCase<T extends string[] | string>(
|
|
40
|
+
input: T,
|
|
41
|
+
toCase: CaseTransformer,
|
|
42
|
+
): T extends string ? string : string[] {
|
|
43
|
+
if (Array.isArray(input)) {
|
|
44
|
+
return input.map(item =>
|
|
45
|
+
CASE_TRANSFORMERS_MAPPING[toCase](item),
|
|
46
|
+
) as T extends string ? string : string[]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return CASE_TRANSFORMERS_MAPPING[toCase](input) as T extends string
|
|
50
|
+
? string
|
|
51
|
+
: string[]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type PlainObject = Record<string, unknown>
|
|
55
|
+
|
|
56
|
+
export function isPlainObject(value: unknown): value is PlainObject {
|
|
57
|
+
return (
|
|
58
|
+
value === Object(value) &&
|
|
59
|
+
!Array.isArray(value) &&
|
|
60
|
+
typeof value !== 'function'
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function matches(patterns: (RegExp | string)[], value: string): boolean {
|
|
65
|
+
return patterns.some(pattern =>
|
|
66
|
+
typeof pattern === 'string' ? pattern === value : pattern.test(value),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function capitalize(value: string): string {
|
|
71
|
+
return value.replace(/^\w/u, character => character.toUpperCase())
|
|
72
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* eslint-disable camelcase -- fixture keys intentionally use snake_case */
|
|
2
|
+
import { describe, expect, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
import { CaseTransformer } from './case-transformer-core.ts'
|
|
5
|
+
import { keysCaseTransformer, keysChangeCase } from './keys-case-transformer.ts'
|
|
6
|
+
|
|
7
|
+
describe('keysCaseTransformer', () => {
|
|
8
|
+
test('transforms keys shallowly by default', () => {
|
|
9
|
+
const input = { nested_object: { inner_key: 1 }, top_level: true }
|
|
10
|
+
|
|
11
|
+
expect(keysCaseTransformer(input, CaseTransformer.Camel)).toEqual({
|
|
12
|
+
nestedObject: { inner_key: 1 },
|
|
13
|
+
topLevel: true,
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('transforms keys deeply when requested', () => {
|
|
18
|
+
const input = { nested_object: { inner_key: 1 } }
|
|
19
|
+
|
|
20
|
+
expect(
|
|
21
|
+
keysCaseTransformer(input, CaseTransformer.Camel, { deep: true }),
|
|
22
|
+
).toEqual({
|
|
23
|
+
nestedObject: { innerKey: 1 },
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('keeps excluded keys unchanged', () => {
|
|
28
|
+
const input = { keep_me: 1, transform_me: 2 }
|
|
29
|
+
|
|
30
|
+
expect(
|
|
31
|
+
keysCaseTransformer(input, CaseTransformer.Camel, {
|
|
32
|
+
deep: true,
|
|
33
|
+
exclude: [/^keep_me$/u],
|
|
34
|
+
}),
|
|
35
|
+
).toEqual({
|
|
36
|
+
keep_me: 1,
|
|
37
|
+
transformMe: 2,
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('skips Date instances', () => {
|
|
42
|
+
const date = new Date('2026-01-01T00:00:00.000Z')
|
|
43
|
+
const input = { created_at: date }
|
|
44
|
+
|
|
45
|
+
expect(
|
|
46
|
+
keysCaseTransformer(input, CaseTransformer.Camel, { deep: true }),
|
|
47
|
+
).toEqual({
|
|
48
|
+
createdAt: date,
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('keysChangeCase', () => {
|
|
54
|
+
test('defaults to deep transformation for backward compatibility', () => {
|
|
55
|
+
const input = { nested_object: { inner_key: 1 } }
|
|
56
|
+
|
|
57
|
+
expect(keysChangeCase(input, CaseTransformer.Camel)).toEqual({
|
|
58
|
+
nestedObject: { innerKey: 1 },
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test('matches keysCaseTransformer for deep snake_case transforms', () => {
|
|
63
|
+
const input = {
|
|
64
|
+
hotel_room_offer: {
|
|
65
|
+
booking_policy: { cancel_until: '2026-06-10' },
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
expect(keysChangeCase(input, CaseTransformer.Snake)).toEqual(
|
|
70
|
+
keysCaseTransformer(input, CaseTransformer.Snake, { deep: true }),
|
|
71
|
+
)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -1,108 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
lower: true,
|
|
13
|
-
strict: true,
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export enum CaseTransformer {
|
|
18
|
-
Camel = 'camelCase',
|
|
19
|
-
Capital = 'capitalCase',
|
|
20
|
-
Param = 'paramCase',
|
|
21
|
-
Pascal = 'pascalCase',
|
|
22
|
-
Slug = 'slugCase',
|
|
23
|
-
Snake = 'snakeCase',
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const CASE_TRANSFORMERS_MAPPING = {
|
|
27
|
-
[CaseTransformer.Camel]: camelCase,
|
|
28
|
-
[CaseTransformer.Capital]: capitalCase,
|
|
29
|
-
[CaseTransformer.Param]: kebabCase,
|
|
30
|
-
[CaseTransformer.Pascal]: pascalCase,
|
|
31
|
-
[CaseTransformer.Slug]: slugCase,
|
|
32
|
-
[CaseTransformer.Snake]: snakeCase,
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function changeCase<T extends string[] | string>(
|
|
36
|
-
input: T,
|
|
37
|
-
toCase: CaseTransformer,
|
|
38
|
-
): T extends string ? string : string[] {
|
|
39
|
-
if (Array.isArray(input)) {
|
|
40
|
-
return input.map(item =>
|
|
41
|
-
CASE_TRANSFORMERS_MAPPING[toCase](item),
|
|
42
|
-
) as T extends string ? string : string[]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return CASE_TRANSFORMERS_MAPPING[toCase](input) as T extends string
|
|
46
|
-
? string
|
|
47
|
-
: string[]
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function matches(patterns: (RegExp | string)[], value: string) {
|
|
51
|
-
return patterns.some(pattern =>
|
|
52
|
-
typeof pattern === 'string' ? pattern === value : pattern.test(value),
|
|
53
|
-
)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
type PlainObject = Record<string, unknown>
|
|
57
|
-
|
|
58
|
-
function isPlainObject(value: unknown): value is PlainObject {
|
|
59
|
-
return (
|
|
60
|
-
value === Object(value) &&
|
|
61
|
-
!Array.isArray(value) &&
|
|
62
|
-
typeof value !== 'function'
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Infer `T` from input, or set `T` explicitly when output type differs (e.g. fixture → domain). */
|
|
67
|
-
export function keysChangeCase<T extends object | readonly unknown[]>(
|
|
68
|
-
obj: T | object | readonly unknown[],
|
|
69
|
-
toCase: CaseTransformer,
|
|
70
|
-
options?: { deep?: boolean; exclude?: (RegExp | string)[] },
|
|
71
|
-
): T
|
|
72
|
-
|
|
73
|
-
/** Unknown input — `Record<string, unknown>` / `unknown[]` instead of `unknown`. */
|
|
74
|
-
export function keysChangeCase(
|
|
75
|
-
obj: unknown,
|
|
76
|
-
toCase: CaseTransformer,
|
|
77
|
-
options?: { deep?: boolean; exclude?: (RegExp | string)[] },
|
|
78
|
-
): PlainObject | unknown[]
|
|
79
|
-
|
|
80
|
-
export function keysChangeCase(
|
|
81
|
-
obj: unknown,
|
|
82
|
-
toCase: CaseTransformer,
|
|
83
|
-
options: { deep?: boolean; exclude?: (RegExp | string)[] } = { deep: true },
|
|
84
|
-
): PlainObject | unknown[] | unknown {
|
|
85
|
-
if (isPlainObject(obj) && !(obj instanceof Date)) {
|
|
86
|
-
return Object.keys(obj).reduce<PlainObject>((result, key) => {
|
|
87
|
-
// Check if key should be excluded from the transformation
|
|
88
|
-
const transformedKey =
|
|
89
|
-
options?.exclude && matches(options.exclude, key)
|
|
90
|
-
? key
|
|
91
|
-
: CASE_TRANSFORMERS_MAPPING[toCase](key)
|
|
92
|
-
|
|
93
|
-
result[transformedKey] = options.deep
|
|
94
|
-
? keysChangeCase(obj[key], toCase, options)
|
|
95
|
-
: obj[key]
|
|
96
|
-
|
|
97
|
-
return result
|
|
98
|
-
}, {})
|
|
99
|
-
} else if (Array.isArray(obj)) {
|
|
100
|
-
return obj.map(item => keysChangeCase(item, toCase, options))
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return obj
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function capitalize(value: string): string {
|
|
107
|
-
return value.replace(/^\w/u, c => c.toUpperCase())
|
|
108
|
-
}
|
|
1
|
+
export {
|
|
2
|
+
CASE_TRANSFORMERS_MAPPING,
|
|
3
|
+
CaseTransformer,
|
|
4
|
+
capitalize,
|
|
5
|
+
changeCase,
|
|
6
|
+
isPlainObject,
|
|
7
|
+
matches,
|
|
8
|
+
type PlainObject,
|
|
9
|
+
} from './case-transformer-core.ts'
|
|
10
|
+
|
|
11
|
+
export { keysChangeCase, keysCaseTransformer } from './keys-case-transformer.ts'
|
package/src/utils/index.ts
CHANGED
|
@@ -35,7 +35,6 @@ export * from './is-equal.ts'
|
|
|
35
35
|
export * from './is-nil.ts'
|
|
36
36
|
export * from './is-object.ts'
|
|
37
37
|
export * from './key-by.ts'
|
|
38
|
-
export * from './keys-case-transformer.ts'
|
|
39
38
|
export * from './lang-default-fallbacks.ts'
|
|
40
39
|
export * from './map-keys.ts'
|
|
41
40
|
export * from './map-values.ts'
|
|
@@ -43,6 +42,7 @@ export * from './merge.ts'
|
|
|
43
42
|
export * from './omit-by.ts'
|
|
44
43
|
export * from './omit.ts'
|
|
45
44
|
export * from './parse-children.ts'
|
|
45
|
+
export * from './pick-by.ts'
|
|
46
46
|
export * from './pick.ts'
|
|
47
47
|
export * from './poller.ts'
|
|
48
48
|
export * from './prepare-ts-query.ts'
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isObject } from './is-object.ts'
|
|
3
|
-
import { mapKeys } from './map-keys.ts'
|
|
1
|
+
import { changeCase, isPlainObject, matches } from './case-transformer-core.ts'
|
|
4
2
|
|
|
3
|
+
import type { CaseTransformer, PlainObject } from './case-transformer-core.ts'
|
|
5
4
|
import type {
|
|
6
5
|
CamelCasedProperties,
|
|
7
6
|
CamelCasedPropertiesDeep,
|
|
@@ -13,46 +12,32 @@ import type {
|
|
|
13
12
|
SnakeCasedPropertiesDeep,
|
|
14
13
|
} from 'type-fest'
|
|
15
14
|
|
|
16
|
-
/**
|
|
17
|
-
* Maps CaseTransformer enum to the corresponding type-fest property transformation (shallow).
|
|
18
|
-
* Capital and Slug cases don't have type-fest equivalents, so they preserve the original type.
|
|
19
|
-
*/
|
|
20
15
|
type TransformProperties<
|
|
21
16
|
T,
|
|
22
17
|
C extends CaseTransformer,
|
|
23
|
-
> = C extends CaseTransformer.Camel
|
|
18
|
+
> = C extends typeof CaseTransformer.Camel
|
|
24
19
|
? CamelCasedProperties<T>
|
|
25
|
-
: C extends CaseTransformer.Snake
|
|
20
|
+
: C extends typeof CaseTransformer.Snake
|
|
26
21
|
? SnakeCasedProperties<T>
|
|
27
|
-
: C extends CaseTransformer.Pascal
|
|
22
|
+
: C extends typeof CaseTransformer.Pascal
|
|
28
23
|
? PascalCasedProperties<T>
|
|
29
|
-
: C extends CaseTransformer.Param
|
|
24
|
+
: C extends typeof CaseTransformer.Param
|
|
30
25
|
? KebabCasedProperties<T>
|
|
31
26
|
: T
|
|
32
27
|
|
|
33
|
-
/**
|
|
34
|
-
* Maps CaseTransformer enum to the corresponding type-fest deep property transformation.
|
|
35
|
-
* Capital and Slug cases don't have type-fest equivalents, so they preserve the original type.
|
|
36
|
-
*/
|
|
37
28
|
type TransformPropertiesDeep<
|
|
38
29
|
T,
|
|
39
30
|
C extends CaseTransformer,
|
|
40
|
-
> = C extends CaseTransformer.Camel
|
|
31
|
+
> = C extends typeof CaseTransformer.Camel
|
|
41
32
|
? CamelCasedPropertiesDeep<T>
|
|
42
|
-
: C extends CaseTransformer.Snake
|
|
33
|
+
: C extends typeof CaseTransformer.Snake
|
|
43
34
|
? SnakeCasedPropertiesDeep<T>
|
|
44
|
-
: C extends CaseTransformer.Pascal
|
|
35
|
+
: C extends typeof CaseTransformer.Pascal
|
|
45
36
|
? PascalCasedPropertiesDeep<T>
|
|
46
|
-
: C extends CaseTransformer.Param
|
|
37
|
+
: C extends typeof CaseTransformer.Param
|
|
47
38
|
? KebabCasedPropertiesDeep<T>
|
|
48
39
|
: T
|
|
49
40
|
|
|
50
|
-
/**
|
|
51
|
-
* Transforms object keys based on the specified case transformer.
|
|
52
|
-
* - Arrays: recursively applies transformation to each element
|
|
53
|
-
* - Objects: applies property transformation (shallow by default, deep if Deep=true)
|
|
54
|
-
* - Primitives: returns as-is
|
|
55
|
-
*/
|
|
56
41
|
export type KeysCaseTransformed<
|
|
57
42
|
T,
|
|
58
43
|
C extends CaseTransformer,
|
|
@@ -71,48 +56,86 @@ export type KeysCaseTransformed<
|
|
|
71
56
|
|
|
72
57
|
export interface KeysCaseTransformerOptions {
|
|
73
58
|
deep?: boolean
|
|
59
|
+
exclude?: (RegExp | string)[]
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function keysCaseTransformerImpl(
|
|
63
|
+
obj: unknown,
|
|
64
|
+
toCase: CaseTransformer,
|
|
65
|
+
options: KeysCaseTransformerOptions,
|
|
66
|
+
): unknown {
|
|
67
|
+
const deep = options.deep ?? false
|
|
68
|
+
|
|
69
|
+
if (Array.isArray(obj)) {
|
|
70
|
+
return obj.map(item => keysCaseTransformerImpl(item, toCase, options))
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (isPlainObject(obj) && !(obj instanceof Date)) {
|
|
74
|
+
return Object.keys(obj).reduce<PlainObject>((accumulator, key) => {
|
|
75
|
+
const transformedKey =
|
|
76
|
+
options.exclude && matches(options.exclude, key)
|
|
77
|
+
? key
|
|
78
|
+
: changeCase(key, toCase)
|
|
79
|
+
|
|
80
|
+
accumulator[transformedKey] = deep
|
|
81
|
+
? keysCaseTransformerImpl(obj[key], toCase, {
|
|
82
|
+
...options,
|
|
83
|
+
deep: true,
|
|
84
|
+
})
|
|
85
|
+
: obj[key]
|
|
86
|
+
|
|
87
|
+
return accumulator
|
|
88
|
+
}, {})
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return obj
|
|
74
92
|
}
|
|
75
93
|
|
|
76
|
-
// Overload: deep transformation
|
|
77
94
|
export function keysCaseTransformer<T, C extends CaseTransformer>(
|
|
78
95
|
obj: T,
|
|
79
96
|
toCase: C,
|
|
80
|
-
options: { deep: true },
|
|
97
|
+
options: KeysCaseTransformerOptions & { deep: true },
|
|
81
98
|
): KeysCaseTransformed<T, C, true>
|
|
82
99
|
|
|
83
|
-
// Overload: shallow transformation (default)
|
|
84
100
|
export function keysCaseTransformer<T, C extends CaseTransformer>(
|
|
85
101
|
obj: T,
|
|
86
102
|
toCase: C,
|
|
87
|
-
options?: { deep?: false },
|
|
103
|
+
options?: KeysCaseTransformerOptions & { deep?: false },
|
|
88
104
|
): KeysCaseTransformed<T, C>
|
|
89
105
|
|
|
90
|
-
// Implementation
|
|
91
106
|
export function keysCaseTransformer<T, C extends CaseTransformer>(
|
|
92
107
|
obj: T,
|
|
93
108
|
toCase: C,
|
|
94
|
-
options
|
|
109
|
+
options: KeysCaseTransformerOptions = {},
|
|
95
110
|
): KeysCaseTransformed<T, C, boolean> {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
return keysCaseTransformerImpl(obj, toCase, options) as KeysCaseTransformed<
|
|
112
|
+
T,
|
|
113
|
+
C,
|
|
114
|
+
boolean
|
|
115
|
+
>
|
|
116
|
+
}
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}
|
|
118
|
+
/** Backward-compatible wrapper defaulting to deep transforms. */
|
|
119
|
+
export function keysChangeCase<T extends object | readonly unknown[]>(
|
|
120
|
+
obj: T | object | readonly unknown[],
|
|
121
|
+
toCase: CaseTransformer,
|
|
122
|
+
options?: KeysCaseTransformerOptions,
|
|
123
|
+
): T
|
|
124
|
+
|
|
125
|
+
/** Unknown input — `Record<string, unknown>` / `unknown[]` instead of `unknown`. */
|
|
126
|
+
export function keysChangeCase(
|
|
127
|
+
obj: unknown,
|
|
128
|
+
toCase: CaseTransformer,
|
|
129
|
+
options?: KeysCaseTransformerOptions,
|
|
130
|
+
): PlainObject | unknown[]
|
|
116
131
|
|
|
117
|
-
|
|
132
|
+
export function keysChangeCase(
|
|
133
|
+
obj: unknown,
|
|
134
|
+
toCase: CaseTransformer,
|
|
135
|
+
options: KeysCaseTransformerOptions = {},
|
|
136
|
+
): PlainObject | unknown[] | unknown {
|
|
137
|
+
return keysCaseTransformerImpl(obj, toCase, {
|
|
138
|
+
deep: options.deep ?? true,
|
|
139
|
+
exclude: options.exclude,
|
|
140
|
+
})
|
|
118
141
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates an object composed of the own enumerable properties of object that predicate returns truthy for.
|
|
3
|
+
* The predicate is invoked with two arguments: (value, key).
|
|
4
|
+
* If predicate is a string, it picks the property with that key name.
|
|
5
|
+
*
|
|
6
|
+
* @param object - The source object
|
|
7
|
+
* @param predicate - The function invoked per property or a string key to pick
|
|
8
|
+
* @returns Returns the new object
|
|
9
|
+
*/
|
|
10
|
+
export function pickBy<T extends object>(
|
|
11
|
+
object: T,
|
|
12
|
+
predicate: string | ((value: T[keyof T], key: keyof T) => boolean),
|
|
13
|
+
): Partial<T> {
|
|
14
|
+
const result = {} as Partial<T>
|
|
15
|
+
|
|
16
|
+
for (const key in object) {
|
|
17
|
+
if (Object.hasOwn(object, key)) {
|
|
18
|
+
const value = object[key]
|
|
19
|
+
|
|
20
|
+
// If predicate is a string, check if key matches
|
|
21
|
+
if (typeof predicate === 'string') {
|
|
22
|
+
// eslint-disable-next-line max-depth
|
|
23
|
+
if (key === predicate) {
|
|
24
|
+
result[key] = value
|
|
25
|
+
}
|
|
26
|
+
} else if (typeof predicate === 'function') {
|
|
27
|
+
// If predicate is a function, use it
|
|
28
|
+
// eslint-disable-next-line max-depth
|
|
29
|
+
if (predicate(value, key)) {
|
|
30
|
+
result[key] = value
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return result
|
|
37
|
+
}
|
package/src/utils/sum-by.ts
CHANGED
|
@@ -6,10 +6,13 @@ export function sumBy<T>(
|
|
|
6
6
|
return 0
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
const getValue =
|
|
9
|
+
const getValue: (item: T) => number | undefined =
|
|
10
10
|
typeof iteratee === 'function'
|
|
11
11
|
? iteratee
|
|
12
|
-
: (item: T) =>
|
|
12
|
+
: (item: T) => {
|
|
13
|
+
const value = item[iteratee]
|
|
14
|
+
return typeof value === 'number' ? value : undefined
|
|
15
|
+
}
|
|
13
16
|
|
|
14
17
|
let result: number | undefined = undefined
|
|
15
18
|
for (const item of array) {
|