Skip to content
This repository was archived by the owner on Jan 2, 2024. It is now read-only.

Commit b60591f

Browse files
feat: support t.record and t.UnknownRecord, closes #27 (#28)
1 parent edf12b5 commit b60591f

File tree

3 files changed

+104
-24
lines changed

3 files changed

+104
-24
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Currently supports (along with their closure under composition):
6060
- `t.partial`
6161
- `t.readonly`
6262
- `t.readonlyArray`
63+
- `t.record`
6364
- `t.recursive`
6465
- `t.string`
6566
- `t.tuple`
@@ -68,6 +69,7 @@ Currently supports (along with their closure under composition):
6869
- `t.union`
6970
- `t.unknown`
7071
- `t.UnknownArray`
72+
- `t.UnknownRecord`
7173
- `t.void`
7274

7375
If you `yarn add monocle-ts io-ts-types` and register the

src/core.ts

+60
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,64 @@ export function fuzzRecursive(
219219
};
220220
}
221221

222+
function recordFuzzFunc<K, V>(maxCount: number) {
223+
return (
224+
ctx: FuzzContext,
225+
n: number,
226+
hk: FuzzerUnit<unknown>,
227+
hv: FuzzerUnit<unknown>
228+
) => {
229+
const ret = Object.create(null);
230+
const r = rng(n);
231+
if (!ctx.mayRecurse() && (hk.mightRecurse || hv.mightRecurse)) {
232+
return ret;
233+
}
234+
const ml = Math.abs(r.int32()) % maxCount;
235+
for (let index = 0; index < ml; index++) {
236+
const k = hk.encode([r.int32(), ctx]) as K;
237+
const kt = typeof k;
238+
if (kt !== 'string') {
239+
throw new Error(
240+
'IOTSF0004: recordFuzzer cannot support non-(string, number, boolean) key types'
241+
);
242+
}
243+
const v = hv.encode([r.int32(), ctx]) as V;
244+
ret[k] = v;
245+
}
246+
return ret;
247+
};
248+
}
249+
250+
export const defaultMaxRecordCount = 5;
251+
252+
const fuzzUnknownRecordWithMaxCount = (maxCount: number) => (
253+
b: t.AnyDictionaryType
254+
): ConcreteFuzzer<unknown, unknown> => {
255+
return {
256+
mightRecurse: false,
257+
children: [t.string, t.unknown],
258+
func: recordFuzzFunc<string, unknown>(maxCount),
259+
};
260+
};
261+
262+
export function unknownRecordFuzzer(maxCount: number = defaultMaxRecordCount) {
263+
return gen(fuzzUnknownRecordWithMaxCount(maxCount), 'AnyDictionaryType');
264+
}
265+
266+
const fuzzRecordWithMaxCount = (maxCount: number) => (
267+
b: t.DictionaryType<t.Any, t.Any>
268+
): ConcreteFuzzer<unknown, unknown> => {
269+
return {
270+
mightRecurse: false,
271+
children: [b.domain, b.codomain],
272+
func: recordFuzzFunc<unknown, unknown>(maxCount),
273+
};
274+
};
275+
276+
export function recordFuzzer(maxCount: number = defaultMaxRecordCount) {
277+
return gen(fuzzRecordWithMaxCount(maxCount), 'DictionaryType');
278+
}
279+
222280
function arrayFuzzFunc(maxLength: number) {
223281
return (ctx: FuzzContext, n: number, h0: FuzzerUnit<unknown>) => {
224282
const ret: unknown[] = [];
@@ -412,6 +470,8 @@ export const coreFuzzers: ReadonlyArray<Fuzzer<unknown, unknown, any>> = [
412470
partialFuzzer(),
413471
arrayFuzzer(),
414472
anyArrayFuzzer(),
473+
recordFuzzer(),
474+
unknownRecordFuzzer(),
415475
gen(fuzzExact, 'ExactType'),
416476
gen(fuzzReadonly, 'ReadonlyType'),
417477
readonlyArrayFuzzer(),

test/tested-types.ts

+42-24
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,23 @@ const RecD2_MutualRecursionArray: t.Type<
9595
next: t.union([t.undefined, RecD1_MutualRecursionReadonlyArray]),
9696
})
9797
);
98+
99+
// tslint:disable-next-line:class-name
100+
interface RecE2_RecordOfRecursion {
101+
rr: Record<'a' | 'b' | 'c' | 'd' | 'e', RecE2_RecordOfRecursion>;
102+
}
103+
// tslint:disable-next-line:variable-name
104+
const RecE2_RecordOfRecursion: t.Type<RecE2_RecordOfRecursion> = t.recursion(
105+
'RecE2_RecordOfRecursion',
106+
() =>
107+
t.type({
108+
rr: t.record(
109+
t.keyof({ a: null, b: null, c: null, d: null, e: null }),
110+
RecE2_RecordOfRecursion
111+
),
112+
})
113+
);
114+
98115
// tslint:disable-next-line:no-any
99116
export const types: Array<t.Type<any>> = [
100117
// Recursive types
@@ -106,6 +123,7 @@ export const types: Array<t.Type<any>> = [
106123
RecE1_ArrayOfRecursion,
107124
RecD1_MutualRecursionReadonlyArray,
108125
RecD2_MutualRecursionArray,
126+
RecE2_RecordOfRecursion,
109127
// Simple 0- or 1-depth types
110128
t.number,
111129
t.string,
@@ -146,7 +164,28 @@ export const types: Array<t.Type<any>> = [
146164
t.readonly(t.tuple([t.string, t.boolean])),
147165
t.readonly(t.type({ s: t.string, j: t.boolean })),
148166

167+
t.UnknownRecord,
168+
t.record(t.string, t.number),
169+
t.record(t.keyof({ 3: null, b: null, true: null }), t.number),
149170
// Complex nested types
171+
t.record(
172+
t.string,
173+
t.union([
174+
t.readonly(
175+
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean })
176+
),
177+
t.readonly(t.partial({ s2: t.string, j: t.boolean })),
178+
])
179+
),
180+
t.record(
181+
t.string,
182+
t.union([
183+
t.readonly(
184+
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean })
185+
),
186+
t.readonly(t.partial({ s2: t.string, j: t.boolean })),
187+
])
188+
),
150189
t.exact(
151190
t.intersection([
152191
t.type({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
@@ -254,33 +293,12 @@ export const unknownTypes: Array<t.Decoder<unknown, unknown>> = [
254293
weirdString,
255294
t.union([t.string, weirdString]),
256295
customStringDecoder,
257-
258-
// TODO - implement these:
259-
t.UnknownRecord,
260-
t.record(t.string, t.number),
261-
t.record(t.number, t.number),
262-
t.record(t.boolean, t.number),
263-
t.record(
264-
t.string,
265-
t.union([
266-
t.readonly(
267-
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean })
268-
),
269-
t.readonly(t.partial({ s2: t.string, j: t.boolean })),
270-
])
271-
),
272-
t.record(
273-
t.string,
274-
t.union([
275-
t.readonly(
276-
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean })
277-
),
278-
t.readonly(t.partial({ s2: t.string, j: t.boolean })),
279-
])
280-
),
281296
];
282297

283298
export const runtimeFailTypes = [
299+
t.record(t.number, t.number),
300+
t.record(t.type({ m: t.number }), t.number),
301+
t.record(t.boolean, t.number),
284302
t.intersection([t.string, t.type({ m: t.number })]),
285303
t.intersection([t.type({ m: t.number }), t.string]),
286304
];

0 commit comments

Comments
 (0)