Skip to content

Commit d4326b5

Browse files
author
Elias Mulhall
committed
array decoder may validate any[]
1 parent 72e8ed6 commit d4326b5

File tree

2 files changed

+54
-23
lines changed

2 files changed

+54
-23
lines changed

src/decoder.ts

+36-23
Original file line numberDiff line numberDiff line change
@@ -174,16 +174,6 @@ export class Decoder<A> {
174174

175175
/**
176176
* Decoder identity function. Useful for incremental decoding.
177-
*
178-
* Example:
179-
* ```
180-
* const json: any = [1, true, 2, 3, 'five', 4, []];
181-
* const jsonArray: any[] = Result.withDefault([], array(anyJson()).run(json));
182-
* const numbers: number[] = Result.successes(jsonArray.map(number().run));
183-
*
184-
* numbers
185-
* // => [1, 2, 3, 4]
186-
* ```
187177
*/
188178
static anyJson = (): Decoder<any> => new Decoder<any>((json: any) => Result.ok(json));
189179

@@ -310,7 +300,9 @@ export class Decoder<A> {
310300

311301
/**
312302
* Decoder for json arrays. Runs `decoder` on each array element, and succeeds
313-
* if all elements are successfully decoded.
303+
* if all elements are successfully decoded. If no `decoder` argument is
304+
* provided then the outer array part of the json is validated but not the
305+
* contents, typing the result as `any[]`.
314306
*
315307
* To decode a single value that is inside of an array see `valueAt`.
316308
*
@@ -321,21 +313,42 @@ export class Decoder<A> {
321313
*
322314
* array(array(boolean())).run([[true], [], [true, false, false]])
323315
* // => {ok: true, result: [[true], [], [true, false, false]]}
316+
*
317+
*
318+
* const validNumbersDecoder = array()
319+
* .map((arr: any[]) => arr.map(number().run))
320+
* .map(Result.successes)
321+
*
322+
* validNumbersDecoder.run([1, true, 2, 3, 'five', 4, []])
323+
* // {ok: true, result: [1, 2, 3, 4]}
324+
*
325+
* validNumbersDecoder.run([false, 'hi', {}])
326+
* // {ok: true, result: []}
327+
*
328+
* validNumbersDecoder.run(false)
329+
* // {ok: false, error: {..., message: "expected an array, got a boolean"}}
324330
* ```
325331
*/
326-
static array = <A>(decoder: Decoder<A>): Decoder<A[]> =>
327-
new Decoder<A[]>(json => {
328-
const decodeValue = (v: any, i: number): DecodeResult<A> =>
329-
Result.mapError(err => prependAt(`[${i}]`, err), decoder.decode(v));
330-
331-
return isJsonArray(json)
332-
? json.reduce(
333-
(acc: DecodeResult<A[]>, v: any, i: number) =>
334-
Result.map2((arr, result) => [...arr, result], acc, decodeValue(v, i)),
335-
Result.ok([])
336-
)
337-
: Result.err({message: expectedGot('an array', json)});
332+
static array(): Decoder<any[]>;
333+
static array<A>(decoder: Decoder<A>): Decoder<A[]>;
334+
static array<A>(decoder?: Decoder<A>) {
335+
return new Decoder(json => {
336+
if (isJsonArray(json) && decoder) {
337+
const decodeValue = (v: any, i: number): DecodeResult<A> =>
338+
Result.mapError(err => prependAt(`[${i}]`, err), decoder.decode(v));
339+
340+
return json.reduce(
341+
(acc: DecodeResult<A[]>, v: any, i: number) =>
342+
Result.map2((arr, result) => [...arr, result], acc, decodeValue(v, i)),
343+
Result.ok([])
344+
);
345+
} else if (isJsonArray(json)) {
346+
return Result.ok(json);
347+
} else {
348+
return Result.err({message: expectedGot('an array', json)});
349+
}
338350
});
351+
}
339352

340353
/**
341354
* Decoder for json objects where the keys are unknown strings, but the values

test/json-decode.test.ts

+18
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,24 @@ describe('array', () => {
286286
});
287287
});
288288
});
289+
290+
it('decodes any array when the array members decoder is not specified', () => {
291+
const validNumbersDecoder = array()
292+
.map((arr: any[]) => arr.map(number().run))
293+
.map(Result.successes);
294+
295+
expect(validNumbersDecoder.run([1, true, 2, 3, 'five', 4, []])).toEqual({
296+
ok: true,
297+
result: [1, 2, 3, 4]
298+
});
299+
300+
expect(validNumbersDecoder.run([false, 'hi', {}])).toEqual({ok: true, result: []});
301+
302+
expect(validNumbersDecoder.run(false)).toMatchObject({
303+
ok: false,
304+
error: {message: 'expected an array, got a boolean'}
305+
});
306+
});
289307
});
290308

291309
describe('dict', () => {

0 commit comments

Comments
 (0)