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

Commit

Permalink
feat: support t.record and t.UnknownRecord, closes #27
Browse files Browse the repository at this point in the history
  • Loading branch information
holvonixAdvay committed Aug 25, 2019
1 parent edf12b5 commit 32757d2
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 24 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Currently supports (along with their closure under composition):
- `t.partial`
- `t.readonly`
- `t.readonlyArray`
- `t.record`
- `t.recursive`
- `t.string`
- `t.tuple`
Expand All @@ -68,6 +69,7 @@ Currently supports (along with their closure under composition):
- `t.union`
- `t.unknown`
- `t.UnknownArray`
- `t.UnknownRecord`
- `t.void`

If you `yarn add monocle-ts io-ts-types` and register the
Expand Down
60 changes: 60 additions & 0 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,64 @@ export function fuzzRecursive(
};
}

function recordFuzzFunc<K, V>(maxCount: number) {
return (
ctx: FuzzContext,
n: number,
hk: FuzzerUnit<unknown>,
hv: FuzzerUnit<unknown>
) => {
const ret = Object.create(null);
const r = rng(n);
if (!ctx.mayRecurse() && (hk.mightRecurse || hv.mightRecurse)) {
return ret;
}
const ml = Math.abs(r.int32()) % maxCount;
for (let index = 0; index < ml; index++) {
const k = hk.encode([r.int32(), ctx]) as K;
const kt = typeof k;
if (kt !== 'string') {
throw new Error(
'IOTSF0004: recordFuzzer cannot support non-(string, number, boolean) key types'
);
}
const v = hv.encode([r.int32(), ctx]) as V;
ret[k] = v;
}
return ret;
};
}

export const defaultMaxRecordCount = 5;

const fuzzUnknownRecordWithMaxCount = (maxCount: number) => (
b: t.AnyDictionaryType
): ConcreteFuzzer<unknown, unknown> => {
return {
mightRecurse: false,
children: [t.string, t.unknown],
func: recordFuzzFunc<string, unknown>(maxCount),
};
};

export function unknownRecordFuzzer(maxCount: number = defaultMaxRecordCount) {
return gen(fuzzUnknownRecordWithMaxCount(maxCount), 'AnyDictionaryType');
}

const fuzzRecordWithMaxCount = (maxCount: number) => (
b: t.DictionaryType<t.Any, t.Any>
): ConcreteFuzzer<unknown, unknown> => {
return {
mightRecurse: false,
children: [b.domain, b.codomain],
func: recordFuzzFunc<unknown, unknown>(maxCount),
};
};

export function recordFuzzer(maxCount: number = defaultMaxRecordCount) {
return gen(fuzzRecordWithMaxCount(maxCount), 'DictionaryType');
}

function arrayFuzzFunc(maxLength: number) {
return (ctx: FuzzContext, n: number, h0: FuzzerUnit<unknown>) => {
const ret: unknown[] = [];
Expand Down Expand Up @@ -412,6 +470,8 @@ export const coreFuzzers: ReadonlyArray<Fuzzer<unknown, unknown, any>> = [
partialFuzzer(),
arrayFuzzer(),
anyArrayFuzzer(),
recordFuzzer(),
unknownRecordFuzzer(),
gen(fuzzExact, 'ExactType'),
gen(fuzzReadonly, 'ReadonlyType'),
readonlyArrayFuzzer(),
Expand Down
66 changes: 42 additions & 24 deletions test/tested-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,23 @@ const RecD2_MutualRecursionArray: t.Type<
next: t.union([t.undefined, RecD1_MutualRecursionReadonlyArray]),
})
);

// tslint:disable-next-line:class-name
interface RecE2_RecordOfRecursion {
rr: Record<'a' | 'b' | 'c' | 'd' | 'e', RecE2_RecordOfRecursion>;
}
// tslint:disable-next-line:variable-name
const RecE2_RecordOfRecursion: t.Type<RecE2_RecordOfRecursion> = t.recursion(
'RecE2_RecordOfRecursion',
() =>
t.type({
rr: t.record(
t.keyof({ a: null, b: null, c: null, d: null, e: null }),
RecE2_RecordOfRecursion
),
})
);

// tslint:disable-next-line:no-any
export const types: Array<t.Type<any>> = [
// Recursive types
Expand All @@ -106,6 +123,7 @@ export const types: Array<t.Type<any>> = [
RecE1_ArrayOfRecursion,
RecD1_MutualRecursionReadonlyArray,
RecD2_MutualRecursionArray,
RecE2_RecordOfRecursion,
// Simple 0- or 1-depth types
t.number,
t.string,
Expand Down Expand Up @@ -146,7 +164,28 @@ export const types: Array<t.Type<any>> = [
t.readonly(t.tuple([t.string, t.boolean])),
t.readonly(t.type({ s: t.string, j: t.boolean })),

t.UnknownRecord,
t.record(t.string, t.number),
t.record(t.keyof({ 3: null, b: null, true: null }), t.number),
// Complex nested types
t.record(
t.string,
t.union([
t.readonly(
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean })
),
t.readonly(t.partial({ s2: t.string, j: t.boolean })),
])
),
t.record(
t.string,
t.union([
t.readonly(
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean })
),
t.readonly(t.partial({ s2: t.string, j: t.boolean })),
])
),
t.exact(
t.intersection([
t.type({ s: t.string, m: t.number, ___0000_extra_: t.boolean }),
Expand Down Expand Up @@ -254,33 +293,12 @@ export const unknownTypes: Array<t.Decoder<unknown, unknown>> = [
weirdString,
t.union([t.string, weirdString]),
customStringDecoder,

// TODO - implement these:
t.UnknownRecord,
t.record(t.string, t.number),
t.record(t.number, t.number),
t.record(t.boolean, t.number),
t.record(
t.string,
t.union([
t.readonly(
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean })
),
t.readonly(t.partial({ s2: t.string, j: t.boolean })),
])
),
t.record(
t.string,
t.union([
t.readonly(
t.partial({ s: t.string, m: t.number, ___0000_extra_: t.boolean })
),
t.readonly(t.partial({ s2: t.string, j: t.boolean })),
])
),
];

export const runtimeFailTypes = [
t.record(t.number, t.number),
t.record(t.type({ m: t.number }), t.number),
t.record(t.boolean, t.number),
t.intersection([t.string, t.type({ m: t.number })]),
t.intersection([t.type({ m: t.number }), t.string]),
];

0 comments on commit 32757d2

Please sign in to comment.