@radio-garden/ditojs-utils 2.85.0-0.5067ad799

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.
Files changed (104) hide show
  1. package/README.md +6 -0
  2. package/package.json +43 -0
  3. package/src/__snapshots__/index.test.js.snap +88 -0
  4. package/src/array/flatten.js +14 -0
  5. package/src/array/flattten.test.js +29 -0
  6. package/src/array/index.js +2 -0
  7. package/src/array/shuffle.js +11 -0
  8. package/src/array/shuffle.test.js +25 -0
  9. package/src/base/base.js +118 -0
  10. package/src/base/base.test.js +590 -0
  11. package/src/base/index.js +1 -0
  12. package/src/class/index.js +1 -0
  13. package/src/class/mixin.js +29 -0
  14. package/src/class/mixin.test.js +70 -0
  15. package/src/dataPath/getEntriesAtDataPath.js +67 -0
  16. package/src/dataPath/getEntriesAtDataPath.test.js +204 -0
  17. package/src/dataPath/getValueAtDataPath.js +45 -0
  18. package/src/dataPath/getValueAtDataPath.test.js +140 -0
  19. package/src/dataPath/index.js +6 -0
  20. package/src/dataPath/normalizeDataPath.js +27 -0
  21. package/src/dataPath/normalizeDataPath.test.js +36 -0
  22. package/src/dataPath/parseDataPath.js +16 -0
  23. package/src/dataPath/parseDataPath.test.js +67 -0
  24. package/src/dataPath/setDataPathEntries.js +8 -0
  25. package/src/dataPath/setDataPathEntries.test.js +36 -0
  26. package/src/dataPath/setValueAtDataPath.js +13 -0
  27. package/src/dataPath/setValueAtDataPath.test.js +34 -0
  28. package/src/function/deprecate.js +9 -0
  29. package/src/function/index.js +4 -0
  30. package/src/function/toAsync.js +9 -0
  31. package/src/function/toAsync.test.js +31 -0
  32. package/src/function/toCallback.js +11 -0
  33. package/src/function/toCallback.test.js +29 -0
  34. package/src/function/toPromiseCallback.js +9 -0
  35. package/src/function/toPromiseCallback.test.js +24 -0
  36. package/src/html/escapeHtml.js +11 -0
  37. package/src/html/escapeHtml.test.js +19 -0
  38. package/src/html/index.js +2 -0
  39. package/src/html/stripHtml.js +23 -0
  40. package/src/html/stripHtml.test.js +37 -0
  41. package/src/index.js +10 -0
  42. package/src/index.test.js +7 -0
  43. package/src/object/asCallback.js +9 -0
  44. package/src/object/clone.js +75 -0
  45. package/src/object/clone.test.js +131 -0
  46. package/src/object/equals.js +36 -0
  47. package/src/object/equals.test.js +269 -0
  48. package/src/object/groupBy.js +15 -0
  49. package/src/object/groupBy.test.js +70 -0
  50. package/src/object/index.js +8 -0
  51. package/src/object/mapKeys.js +7 -0
  52. package/src/object/mapKeys.test.js +16 -0
  53. package/src/object/mapValues.js +9 -0
  54. package/src/object/mapValues.test.js +38 -0
  55. package/src/object/mergeDeeply.js +47 -0
  56. package/src/object/mergeDeeply.test.js +152 -0
  57. package/src/object/pick.js +11 -0
  58. package/src/object/pick.test.js +23 -0
  59. package/src/object/pickBy.js +11 -0
  60. package/src/object/pickBy.test.js +48 -0
  61. package/src/promise/index.js +2 -0
  62. package/src/promise/mapConcurrently.js +33 -0
  63. package/src/promise/mapSequentially.js +9 -0
  64. package/src/string/camelize.js +14 -0
  65. package/src/string/camelize.test.js +37 -0
  66. package/src/string/capitalize.js +7 -0
  67. package/src/string/capitalize.test.js +33 -0
  68. package/src/string/decamelize.js +27 -0
  69. package/src/string/decamelize.test.js +83 -0
  70. package/src/string/deindent.js +69 -0
  71. package/src/string/deindent.test.js +181 -0
  72. package/src/string/escapeRegexp.js +3 -0
  73. package/src/string/format.js +109 -0
  74. package/src/string/format.test.js +196 -0
  75. package/src/string/formatDate.js +13 -0
  76. package/src/string/formatDate.test.js +28 -0
  77. package/src/string/getCommonPrefix.js +35 -0
  78. package/src/string/getCommonPrefix.test.js +23 -0
  79. package/src/string/index.js +15 -0
  80. package/src/string/isAbsoluteUrl.js +7 -0
  81. package/src/string/isAbsoluteUrl.test.js +15 -0
  82. package/src/string/isCreditCard.js +21 -0
  83. package/src/string/isCreditCard.test.js +50 -0
  84. package/src/string/isDomain.js +9 -0
  85. package/src/string/isDomain.test.js +15 -0
  86. package/src/string/isEmail.js +6 -0
  87. package/src/string/isEmail.test.js +37 -0
  88. package/src/string/isHostname.js +8 -0
  89. package/src/string/isHostname.test.js +12 -0
  90. package/src/string/isUrl.js +23 -0
  91. package/src/string/isUrl.test.js +1595 -0
  92. package/src/string/labelize.js +17 -0
  93. package/src/string/labelize.test.js +39 -0
  94. package/src/timer/debounce.js +34 -0
  95. package/src/timer/debounce.test.js +101 -0
  96. package/src/timer/debounceAsync.js +60 -0
  97. package/src/timer/debounceAsync.test.js +143 -0
  98. package/src/timer/index.js +2 -0
  99. package/types/index.d.ts +939 -0
  100. package/types/tests/base.test-d.ts +172 -0
  101. package/types/tests/datapath.test-d.ts +75 -0
  102. package/types/tests/function.test-d.ts +137 -0
  103. package/types/tests/object.test-d.ts +190 -0
  104. package/types/tests/promise.test-d.ts +66 -0
@@ -0,0 +1,172 @@
1
+ import { assertType, describe, expectTypeOf, it } from 'vitest'
2
+ import type {
3
+ isArray,
4
+ isPlainObject,
5
+ isFunction,
6
+ isNumber,
7
+ isString,
8
+ isBoolean,
9
+ isDate,
10
+ isRegExp,
11
+ isPromise,
12
+ isAsync,
13
+ isArrayLike,
14
+ asObject,
15
+ asArray,
16
+ asFunction
17
+ } from '../index.d.ts'
18
+
19
+ describe('type guards', () => {
20
+ it('isArray narrows unknown to any[]', () => {
21
+ const guard = {} as typeof isArray
22
+ const val: unknown = []
23
+ if (guard(val)) {
24
+ expectTypeOf(val).toEqualTypeOf<any[]>()
25
+ }
26
+ })
27
+
28
+ it('isPlainObject narrows union to record', () => {
29
+ const guard = {} as typeof isPlainObject
30
+ const val: string | Record<string, unknown> = {} as any
31
+ if (guard(val)) {
32
+ expectTypeOf(val).not.toBeAny()
33
+ expectTypeOf(val).toEqualTypeOf<Record<string, unknown>>()
34
+ }
35
+ })
36
+
37
+ it('isFunction narrows to callable', () => {
38
+ const guard = {} as typeof isFunction
39
+ const val: string | (() => void) = {} as any
40
+ if (guard(val)) {
41
+ expectTypeOf(val).not.toBeAny()
42
+ expectTypeOf(val).toBeCallableWith()
43
+ }
44
+ })
45
+
46
+ it('isNumber narrows union', () => {
47
+ const guard = {} as typeof isNumber
48
+ const val: string | number = {} as any
49
+ if (guard(val)) {
50
+ expectTypeOf(val).not.toBeAny()
51
+ expectTypeOf(val).toBeNumber()
52
+ }
53
+ })
54
+
55
+ it('isString narrows union', () => {
56
+ const guard = {} as typeof isString
57
+ const val: number | string = {} as any
58
+ if (guard(val)) {
59
+ expectTypeOf(val).not.toBeAny()
60
+ assertType<string | String>(val)
61
+ }
62
+ })
63
+
64
+ it('isBoolean narrows union', () => {
65
+ const guard = {} as typeof isBoolean
66
+ const val: string | boolean = {} as any
67
+ if (guard(val)) {
68
+ expectTypeOf(val).not.toBeAny()
69
+ expectTypeOf(val).toBeBoolean()
70
+ }
71
+ })
72
+
73
+ it('isDate narrows to Date', () => {
74
+ const guard = {} as typeof isDate
75
+ const val: string | Date = {} as any
76
+ if (guard(val)) {
77
+ expectTypeOf(val).not.toBeAny()
78
+ expectTypeOf(val).toEqualTypeOf<Date>()
79
+ }
80
+ })
81
+
82
+ it('isRegExp narrows to RegExp', () => {
83
+ const guard = {} as typeof isRegExp
84
+ const val: string | RegExp = {} as any
85
+ if (guard(val)) {
86
+ expectTypeOf(val).not.toBeAny()
87
+ expectTypeOf(val).toEqualTypeOf<RegExp>()
88
+ }
89
+ })
90
+
91
+ it('isPromise narrows to Promise', () => {
92
+ const guard = {} as typeof isPromise
93
+ const val: string | Promise<number> = {} as any
94
+ if (guard(val)) {
95
+ expectTypeOf(val).not.toBeAny()
96
+ assertType<Promise<any>>(val)
97
+ }
98
+ })
99
+
100
+ it('isAsync narrows to async function', () => {
101
+ const guard = {} as typeof isAsync
102
+ const val: (() => void) | (() => Promise<string>) = {} as any
103
+ if (guard(val)) {
104
+ expectTypeOf(val).not.toBeAny()
105
+ assertType<(...args: any[]) => Promise<any>>(val)
106
+ }
107
+ })
108
+
109
+ it('isArrayLike narrows to ArrayLike', () => {
110
+ const guard = {} as typeof isArrayLike
111
+ const val: string | ArrayLike<number> = {} as any
112
+ if (guard(val)) {
113
+ expectTypeOf(val).not.toBeAny()
114
+ expectTypeOf(val).toHaveProperty('length')
115
+ }
116
+ })
117
+ })
118
+
119
+ describe('asObject', () => {
120
+ it('preserves object types', () => {
121
+ const fn = {} as typeof asObject
122
+ const obj = { a: 1 }
123
+ const result = fn(obj)
124
+ expectTypeOf(result).not.toBeAny()
125
+ expectTypeOf(result).toHaveProperty('a')
126
+ })
127
+
128
+ it('returns null/undefined as-is', () => {
129
+ const fn = {} as typeof asObject
130
+ expectTypeOf(fn(null)).toBeNull()
131
+ expectTypeOf(fn(undefined)).toBeUndefined()
132
+ })
133
+ })
134
+
135
+ describe('asArray', () => {
136
+ it('passes arrays through unchanged', () => {
137
+ const fn = {} as typeof asArray
138
+ const arr = [1, 2, 3]
139
+ const result = fn(arr)
140
+ expectTypeOf(result).not.toBeAny()
141
+ expectTypeOf(result).toEqualTypeOf<number[]>()
142
+ })
143
+
144
+ it('wraps non-array in array', () => {
145
+ const fn = {} as typeof asArray
146
+ const result = fn('hello')
147
+ expectTypeOf(result).not.toBeAny()
148
+ expectTypeOf(result).toEqualTypeOf<string[]>()
149
+ })
150
+
151
+ it('returns empty array for undefined', () => {
152
+ const fn = {} as typeof asArray
153
+ expectTypeOf(fn(undefined)).toEqualTypeOf<never[]>()
154
+ })
155
+ })
156
+
157
+ describe('asFunction', () => {
158
+ it('passes functions through unchanged', () => {
159
+ const fn = {} as typeof asFunction
160
+ const cb = (x: number) => x * 2
161
+ const result = fn(cb)
162
+ expectTypeOf(result).not.toBeAny()
163
+ expectTypeOf(result).toEqualTypeOf<(x: number) => number>()
164
+ })
165
+
166
+ it('wraps non-function in thunk', () => {
167
+ const fn = {} as typeof asFunction
168
+ const result = fn(42)
169
+ expectTypeOf(result).not.toBeAny()
170
+ expectTypeOf(result).toEqualTypeOf<() => number>()
171
+ })
172
+ })
@@ -0,0 +1,75 @@
1
+ import { describe, expectTypeOf, it } from 'vitest'
2
+ import type {
3
+ getValueAtDataPath,
4
+ getEntriesAtDataPath,
5
+ setValueAtDataPath,
6
+ setDataPathEntries,
7
+ normalizeDataPath,
8
+ parseDataPath
9
+ } from '../index.d.ts'
10
+
11
+ describe('getValueAtDataPath', () => {
12
+ it('error handler receives correct params', () => {
13
+ const fn = {} as typeof getValueAtDataPath
14
+ fn({}, 'a.b', (obj, part, index) => {
15
+ expectTypeOf(part).not.toBeAny()
16
+ expectTypeOf(part).toBeString()
17
+ expectTypeOf(index).not.toBeAny()
18
+ expectTypeOf(index).toBeNumber()
19
+ })
20
+ })
21
+ })
22
+
23
+ describe('getEntriesAtDataPath', () => {
24
+ it('error handler receives correct params', () => {
25
+ const fn = {} as typeof getEntriesAtDataPath
26
+ fn({}, 'items.*', (obj, part, index) => {
27
+ expectTypeOf(part).not.toBeAny()
28
+ expectTypeOf(part).toBeString()
29
+ expectTypeOf(index).not.toBeAny()
30
+ expectTypeOf(index).toBeNumber()
31
+ })
32
+ })
33
+ })
34
+
35
+ describe('setValueAtDataPath', () => {
36
+ it('preserves the object type', () => {
37
+ const fn = {} as typeof setValueAtDataPath
38
+ const obj = { a: { b: 1 } }
39
+ const result = fn(obj, 'a.b', 2)
40
+ expectTypeOf(result).not.toBeAny()
41
+ expectTypeOf(result).toEqualTypeOf<typeof obj>()
42
+ })
43
+ })
44
+
45
+ describe('setDataPathEntries', () => {
46
+ it('preserves the object type', () => {
47
+ const fn = {} as typeof setDataPathEntries
48
+ const obj = { x: 1, y: 2 }
49
+ const result = fn(obj, { x: 10 })
50
+ expectTypeOf(result).not.toBeAny()
51
+ expectTypeOf(result).toEqualTypeOf<typeof obj>()
52
+ })
53
+ })
54
+
55
+ describe('parseDataPath', () => {
56
+ it('returns string array from string or array input', () => {
57
+ const fn = {} as typeof parseDataPath
58
+ const fromString = fn('a.b.c')
59
+ const fromArray = fn(['a', 'b'])
60
+ expectTypeOf(fromString).not.toBeAny()
61
+ expectTypeOf(fromString).toEqualTypeOf<string[]>()
62
+ expectTypeOf(fromArray).toEqualTypeOf<string[]>()
63
+ })
64
+ })
65
+
66
+ describe('normalizeDataPath', () => {
67
+ it('returns string from string or array input', () => {
68
+ const fn = {} as typeof normalizeDataPath
69
+ const fromString = fn('a.b')
70
+ const fromArray = fn(['a', 'b'])
71
+ expectTypeOf(fromString).not.toBeAny()
72
+ expectTypeOf(fromString).toBeString()
73
+ expectTypeOf(fromArray).toBeString()
74
+ })
75
+ })
@@ -0,0 +1,137 @@
1
+ import { assertType, describe, expectTypeOf, it } from 'vitest'
2
+ import type {
3
+ debounce,
4
+ debounceAsync,
5
+ toAsync,
6
+ toCallback,
7
+ toPromiseCallback
8
+ } from '../index.d.ts'
9
+
10
+ describe('debounce', () => {
11
+ it('preserves original function signature', () => {
12
+ const fn = {} as typeof debounce
13
+ const original = (x: number, y: string) => x + y.length
14
+ const debounced = fn(original, 100)
15
+ expectTypeOf(debounced).not.toBeAny()
16
+ expectTypeOf(debounced).toBeCallableWith(1, 'hello')
17
+ expectTypeOf(debounced(1, 'a')).toBeNumber()
18
+ })
19
+
20
+ it('adds cancel method', () => {
21
+ const fn = {} as typeof debounce
22
+ const debounced = fn(() => {}, 100)
23
+ expectTypeOf(debounced.cancel).not.toBeAny()
24
+ expectTypeOf(debounced.cancel).toBeFunction()
25
+ expectTypeOf(debounced.cancel()).toBeBoolean()
26
+ })
27
+
28
+ it('accepts options object', () => {
29
+ const fn = {} as typeof debounce
30
+ fn((x: number) => x, { delay: 100, immediate: true })
31
+ })
32
+
33
+ it('rejects wrong argument types', () => {
34
+ const fn = {} as typeof debounce
35
+ const debounced = fn((x: number) => x, 100)
36
+ // @ts-expect-error - string not assignable to number
37
+ debounced('wrong')
38
+ })
39
+ })
40
+
41
+ describe('debounceAsync', () => {
42
+ it('preserves async function signature', () => {
43
+ const fn = {} as typeof debounceAsync
44
+ const original = async (id: number) => ({
45
+ id,
46
+ name: 'test'
47
+ })
48
+ const debounced = fn(original, 200)
49
+ expectTypeOf(debounced).not.toBeAny()
50
+ expectTypeOf(debounced).toBeCallableWith(1)
51
+ expectTypeOf(debounced(1))
52
+ .resolves.toHaveProperty('name')
53
+ })
54
+ })
55
+
56
+ describe('toAsync', () => {
57
+ it('converts error-first callback to promise', () => {
58
+ const fn = {} as typeof toAsync
59
+ const readFile = (
60
+ path: string,
61
+ cb: (err: any, data: Buffer) => void
62
+ ) => {}
63
+ const asyncReadFile = fn(readFile)
64
+ expectTypeOf(asyncReadFile).not.toBeAny()
65
+ expectTypeOf(asyncReadFile).toBeCallableWith('test.txt')
66
+ expectTypeOf(asyncReadFile('x'))
67
+ .resolves.toEqualTypeOf<Buffer>()
68
+ })
69
+
70
+ it('converts void callback to promise', () => {
71
+ const fn = {} as typeof toAsync
72
+ const doThing = (cb: (err?: any) => void) => {}
73
+ const asyncDoThing = fn(doThing)
74
+ expectTypeOf(asyncDoThing).not.toBeAny()
75
+ expectTypeOf(asyncDoThing())
76
+ .resolves.toEqualTypeOf<void>()
77
+ })
78
+
79
+ it('handles multiple arguments', () => {
80
+ const fn = {} as typeof toAsync
81
+ const write = (
82
+ path: string,
83
+ data: string,
84
+ cb: (err: any, written: number) => void
85
+ ) => {}
86
+ const asyncWrite = fn(write)
87
+ expectTypeOf(asyncWrite).not.toBeAny()
88
+ expectTypeOf(asyncWrite).toBeCallableWith('f', 'data')
89
+ expectTypeOf(asyncWrite('f', 'd'))
90
+ .resolves.toBeNumber()
91
+ })
92
+ })
93
+
94
+ describe('toCallback', () => {
95
+ it('converts async fn to callback style', () => {
96
+ const fn = {} as typeof toCallback
97
+ const asyncFn = async (x: number): Promise<string> => `${x}`
98
+ const cbFn = fn(asyncFn)
99
+ expectTypeOf(cbFn).not.toBeAny()
100
+ expectTypeOf(cbFn).toBeCallableWith(
101
+ 42,
102
+ (err: any, result: string) => {}
103
+ )
104
+ })
105
+
106
+ it('handles void return', () => {
107
+ const fn = {} as typeof toCallback
108
+ const asyncFn = async (x: string): Promise<void> => {}
109
+ const cbFn = fn(asyncFn)
110
+ expectTypeOf(cbFn).not.toBeAny()
111
+ expectTypeOf(cbFn).toBeCallableWith(
112
+ 'hello',
113
+ (err: any) => {}
114
+ )
115
+ })
116
+ })
117
+
118
+ describe('toPromiseCallback', () => {
119
+ it('returns callback matching resolve/reject types', () => {
120
+ const fn = {} as typeof toPromiseCallback
121
+ const cb = fn<string, Error>(
122
+ value => {
123
+ expectTypeOf(value).not.toBeAny()
124
+ expectTypeOf(value).toBeString()
125
+ },
126
+ reason => {
127
+ expectTypeOf(reason).not.toBeAny()
128
+ expectTypeOf(reason).toEqualTypeOf<Error>()
129
+ }
130
+ )
131
+ expectTypeOf(cb).not.toBeAny()
132
+ expectTypeOf(cb).toBeCallableWith(
133
+ new Error(),
134
+ 'result'
135
+ )
136
+ })
137
+ })
@@ -0,0 +1,190 @@
1
+ import { describe, expectTypeOf, it } from 'vitest'
2
+ import type {
3
+ clone,
4
+ groupBy,
5
+ mergeDeeply,
6
+ assignDeeply,
7
+ pick,
8
+ pickBy,
9
+ mapKeys,
10
+ mapValues
11
+ } from '../index.d.ts'
12
+
13
+ describe('clone', () => {
14
+ it('preserves the input type', () => {
15
+ const fn = {} as typeof clone
16
+ const obj = { a: 1, b: 'hello' }
17
+ const result = fn(obj)
18
+ expectTypeOf(result).not.toBeAny()
19
+ expectTypeOf(result).toEqualTypeOf<typeof obj>()
20
+ })
21
+
22
+ it('processValue callback receives correct type', () => {
23
+ const fn = {} as typeof clone
24
+ fn(
25
+ { x: 1 },
26
+ {
27
+ processValue(value) {
28
+ expectTypeOf(value).not.toBeAny()
29
+ expectTypeOf(value).toEqualTypeOf<{ x: number }>()
30
+ return value
31
+ }
32
+ }
33
+ )
34
+ })
35
+ })
36
+
37
+ describe('groupBy', () => {
38
+ it('infers callback param type from array', () => {
39
+ const fn = {} as typeof groupBy
40
+ const items = [
41
+ { name: 'a', category: 'x' },
42
+ { name: 'b', category: 'y' }
43
+ ]
44
+ fn(items, item => {
45
+ expectTypeOf(item).not.toBeAny()
46
+ expectTypeOf(item).toEqualTypeOf<{
47
+ name: string
48
+ category: string
49
+ }>()
50
+ return item.category
51
+ })
52
+ })
53
+
54
+ it('accepts property key shorthand', () => {
55
+ const fn = {} as typeof groupBy
56
+ const items = [{ type: 'a' as const, v: 1 }]
57
+ const result = fn(items, 'type')
58
+ expectTypeOf(result).not.toBeAny()
59
+ expectTypeOf(result).toEqualTypeOf<
60
+ Record<string, { type: 'a'; v: number }[]>
61
+ >()
62
+ })
63
+
64
+ it('works with record input', () => {
65
+ const fn = {} as typeof groupBy
66
+ const obj: Record<string, { group: string }> = {}
67
+ fn(obj, item => {
68
+ expectTypeOf(item).not.toBeAny()
69
+ expectTypeOf(item).toEqualTypeOf<{ group: string }>()
70
+ return item.group
71
+ })
72
+ })
73
+ })
74
+
75
+ describe('mergeDeeply', () => {
76
+ it('produces intersection of two args', () => {
77
+ const fn = {} as typeof mergeDeeply
78
+ const result = fn(
79
+ {} as { a: number },
80
+ {} as { b: string }
81
+ )
82
+ expectTypeOf(result).not.toBeAny()
83
+ expectTypeOf(result).toHaveProperty('a')
84
+ expectTypeOf(result).toHaveProperty('b')
85
+ })
86
+
87
+ it('produces intersection of three args', () => {
88
+ const fn = {} as typeof mergeDeeply
89
+ const result = fn(
90
+ {} as { a: number },
91
+ {} as { b: string },
92
+ {} as { c: boolean }
93
+ )
94
+ expectTypeOf(result).not.toBeAny()
95
+ expectTypeOf(result).toHaveProperty('a')
96
+ expectTypeOf(result).toHaveProperty('b')
97
+ expectTypeOf(result).toHaveProperty('c')
98
+ })
99
+ })
100
+
101
+ describe('assignDeeply', () => {
102
+ it('produces intersection of two args', () => {
103
+ const fn = {} as typeof assignDeeply
104
+ const result = fn(
105
+ {} as { a: number },
106
+ {} as { b: string }
107
+ )
108
+ expectTypeOf(result).not.toBeAny()
109
+ expectTypeOf(result).toHaveProperty('a')
110
+ expectTypeOf(result).toHaveProperty('b')
111
+ })
112
+ })
113
+
114
+ describe('pick', () => {
115
+ it('returns union of argument types', () => {
116
+ const fn = {} as typeof pick
117
+ const result = fn(
118
+ {} as number | undefined,
119
+ {} as string
120
+ )
121
+ expectTypeOf(result).not.toBeAny()
122
+ expectTypeOf(result).toEqualTypeOf<number | undefined | string>()
123
+ })
124
+ })
125
+
126
+ describe('pickBy', () => {
127
+ it('callback receives value, key, and object', () => {
128
+ const fn = {} as typeof pickBy
129
+ const obj: Record<string, number> = {}
130
+ fn(obj, (value, key, source) => {
131
+ expectTypeOf(value).not.toBeAny()
132
+ expectTypeOf(value).toBeNumber()
133
+ expectTypeOf(key).toBeString()
134
+ expectTypeOf(source).toEqualTypeOf(obj)
135
+ return value > 1
136
+ })
137
+ })
138
+
139
+ it('returns partial of input type', () => {
140
+ const fn = {} as typeof pickBy
141
+ const obj = {} as { a: number; b: number }
142
+ const result = fn(obj, () => true)
143
+ expectTypeOf(result).not.toBeAny()
144
+ expectTypeOf(result).toEqualTypeOf<Partial<{ a: number; b: number }>>()
145
+ })
146
+ })
147
+
148
+ describe('mapKeys', () => {
149
+ it('callback receives key, value, and object', () => {
150
+ const fn = {} as typeof mapKeys
151
+ const obj: Record<string, number> = {}
152
+ fn(obj, (key, value, source) => {
153
+ expectTypeOf(value).not.toBeAny()
154
+ expectTypeOf(key).toBeString()
155
+ expectTypeOf(value).toBeNumber()
156
+ expectTypeOf(source).toEqualTypeOf(obj)
157
+ return `prefix_${key}`
158
+ })
159
+ })
160
+ })
161
+
162
+ describe('mapValues', () => {
163
+ it('callback receives value, key, and object', () => {
164
+ const fn = {} as typeof mapValues
165
+ const obj: Record<string, number> = {}
166
+ fn(obj, (value, key, source) => {
167
+ expectTypeOf(value).not.toBeAny()
168
+ expectTypeOf(value).toBeNumber()
169
+ expectTypeOf(key).toBeString()
170
+ expectTypeOf(source).toEqualTypeOf(obj)
171
+ return String(value)
172
+ })
173
+ })
174
+
175
+ it('result values match callback return type', () => {
176
+ const fn = {} as typeof mapValues
177
+ const obj: Record<string, number> = {}
178
+ const result = fn(obj, value => String(value))
179
+ expectTypeOf(result).not.toBeAny()
180
+ expectTypeOf(result).toEqualTypeOf<Record<string, string>>()
181
+ })
182
+
183
+ it('property shorthand extracts nested value', () => {
184
+ const fn = {} as typeof mapValues
185
+ const obj: Record<string, { nested: number }> = {}
186
+ const result = fn(obj, 'nested')
187
+ expectTypeOf(result).not.toBeAny()
188
+ expectTypeOf(result).toEqualTypeOf<Record<string, number>>()
189
+ })
190
+ })
@@ -0,0 +1,66 @@
1
+ import { describe, expectTypeOf, it } from 'vitest'
2
+ import type { mapConcurrently, mapSequentially } from '../index.d.ts'
3
+
4
+ describe('mapConcurrently', () => {
5
+ it('infers callback param from input array type', () => {
6
+ const fn = {} as typeof mapConcurrently
7
+ fn([1, 2, 3], async (value, index, array) => {
8
+ expectTypeOf(value).not.toBeAny()
9
+ expectTypeOf(value).toBeNumber()
10
+ expectTypeOf(index).toBeNumber()
11
+ expectTypeOf(array).toEqualTypeOf<number[]>()
12
+ return String(value)
13
+ })
14
+ })
15
+
16
+ it('result type matches callback return', () => {
17
+ const fn = {} as typeof mapConcurrently
18
+ const result = fn(
19
+ [1, 2],
20
+ async value => ({ doubled: value * 2 })
21
+ )
22
+ expectTypeOf(result)
23
+ .resolves.items.not.toBeAny()
24
+ expectTypeOf(result)
25
+ .resolves.items.toEqualTypeOf<{ doubled: number }>()
26
+ })
27
+
28
+ it('accepts promise input', () => {
29
+ const fn = {} as typeof mapConcurrently
30
+ const input = Promise.resolve(['a', 'b'])
31
+ fn(input, async value => {
32
+ expectTypeOf(value).not.toBeAny()
33
+ expectTypeOf(value).toBeString()
34
+ return value.length
35
+ })
36
+ })
37
+
38
+ it('accepts synchronous callback', () => {
39
+ const fn = {} as typeof mapConcurrently
40
+ const result = fn([1, 2], value => `${value}`)
41
+ expectTypeOf(result).resolves.items.not.toBeAny()
42
+ expectTypeOf(result).resolves.items.toBeString()
43
+ })
44
+ })
45
+
46
+ describe('mapSequentially', () => {
47
+ it('infers callback param from input array type', () => {
48
+ const fn = {} as typeof mapSequentially
49
+ fn(['a', 'b'], async (value, index) => {
50
+ expectTypeOf(value).not.toBeAny()
51
+ expectTypeOf(value).toBeString()
52
+ expectTypeOf(index).toBeNumber()
53
+ return value.length
54
+ })
55
+ })
56
+
57
+ it('result type matches callback return', () => {
58
+ const fn = {} as typeof mapSequentially
59
+ const result = fn(
60
+ [{ id: 1 }],
61
+ async item => item.id
62
+ )
63
+ expectTypeOf(result).resolves.items.not.toBeAny()
64
+ expectTypeOf(result).resolves.items.toBeNumber()
65
+ })
66
+ })