@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.
- package/README.md +6 -0
- package/package.json +43 -0
- package/src/__snapshots__/index.test.js.snap +88 -0
- package/src/array/flatten.js +14 -0
- package/src/array/flattten.test.js +29 -0
- package/src/array/index.js +2 -0
- package/src/array/shuffle.js +11 -0
- package/src/array/shuffle.test.js +25 -0
- package/src/base/base.js +118 -0
- package/src/base/base.test.js +590 -0
- package/src/base/index.js +1 -0
- package/src/class/index.js +1 -0
- package/src/class/mixin.js +29 -0
- package/src/class/mixin.test.js +70 -0
- package/src/dataPath/getEntriesAtDataPath.js +67 -0
- package/src/dataPath/getEntriesAtDataPath.test.js +204 -0
- package/src/dataPath/getValueAtDataPath.js +45 -0
- package/src/dataPath/getValueAtDataPath.test.js +140 -0
- package/src/dataPath/index.js +6 -0
- package/src/dataPath/normalizeDataPath.js +27 -0
- package/src/dataPath/normalizeDataPath.test.js +36 -0
- package/src/dataPath/parseDataPath.js +16 -0
- package/src/dataPath/parseDataPath.test.js +67 -0
- package/src/dataPath/setDataPathEntries.js +8 -0
- package/src/dataPath/setDataPathEntries.test.js +36 -0
- package/src/dataPath/setValueAtDataPath.js +13 -0
- package/src/dataPath/setValueAtDataPath.test.js +34 -0
- package/src/function/deprecate.js +9 -0
- package/src/function/index.js +4 -0
- package/src/function/toAsync.js +9 -0
- package/src/function/toAsync.test.js +31 -0
- package/src/function/toCallback.js +11 -0
- package/src/function/toCallback.test.js +29 -0
- package/src/function/toPromiseCallback.js +9 -0
- package/src/function/toPromiseCallback.test.js +24 -0
- package/src/html/escapeHtml.js +11 -0
- package/src/html/escapeHtml.test.js +19 -0
- package/src/html/index.js +2 -0
- package/src/html/stripHtml.js +23 -0
- package/src/html/stripHtml.test.js +37 -0
- package/src/index.js +10 -0
- package/src/index.test.js +7 -0
- package/src/object/asCallback.js +9 -0
- package/src/object/clone.js +75 -0
- package/src/object/clone.test.js +131 -0
- package/src/object/equals.js +36 -0
- package/src/object/equals.test.js +269 -0
- package/src/object/groupBy.js +15 -0
- package/src/object/groupBy.test.js +70 -0
- package/src/object/index.js +8 -0
- package/src/object/mapKeys.js +7 -0
- package/src/object/mapKeys.test.js +16 -0
- package/src/object/mapValues.js +9 -0
- package/src/object/mapValues.test.js +38 -0
- package/src/object/mergeDeeply.js +47 -0
- package/src/object/mergeDeeply.test.js +152 -0
- package/src/object/pick.js +11 -0
- package/src/object/pick.test.js +23 -0
- package/src/object/pickBy.js +11 -0
- package/src/object/pickBy.test.js +48 -0
- package/src/promise/index.js +2 -0
- package/src/promise/mapConcurrently.js +33 -0
- package/src/promise/mapSequentially.js +9 -0
- package/src/string/camelize.js +14 -0
- package/src/string/camelize.test.js +37 -0
- package/src/string/capitalize.js +7 -0
- package/src/string/capitalize.test.js +33 -0
- package/src/string/decamelize.js +27 -0
- package/src/string/decamelize.test.js +83 -0
- package/src/string/deindent.js +69 -0
- package/src/string/deindent.test.js +181 -0
- package/src/string/escapeRegexp.js +3 -0
- package/src/string/format.js +109 -0
- package/src/string/format.test.js +196 -0
- package/src/string/formatDate.js +13 -0
- package/src/string/formatDate.test.js +28 -0
- package/src/string/getCommonPrefix.js +35 -0
- package/src/string/getCommonPrefix.test.js +23 -0
- package/src/string/index.js +15 -0
- package/src/string/isAbsoluteUrl.js +7 -0
- package/src/string/isAbsoluteUrl.test.js +15 -0
- package/src/string/isCreditCard.js +21 -0
- package/src/string/isCreditCard.test.js +50 -0
- package/src/string/isDomain.js +9 -0
- package/src/string/isDomain.test.js +15 -0
- package/src/string/isEmail.js +6 -0
- package/src/string/isEmail.test.js +37 -0
- package/src/string/isHostname.js +8 -0
- package/src/string/isHostname.test.js +12 -0
- package/src/string/isUrl.js +23 -0
- package/src/string/isUrl.test.js +1595 -0
- package/src/string/labelize.js +17 -0
- package/src/string/labelize.test.js +39 -0
- package/src/timer/debounce.js +34 -0
- package/src/timer/debounce.test.js +101 -0
- package/src/timer/debounceAsync.js +60 -0
- package/src/timer/debounceAsync.test.js +143 -0
- package/src/timer/index.js +2 -0
- package/types/index.d.ts +939 -0
- package/types/tests/base.test-d.ts +172 -0
- package/types/tests/datapath.test-d.ts +75 -0
- package/types/tests/function.test-d.ts +137 -0
- package/types/tests/object.test-d.ts +190 -0
- 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
|
+
})
|