Skip to content

Commit 5947568

Browse files
authored
Merge pull request #14 from mojotech/em/simple-errors
Simplify error messages related to unexpected data type
2 parents 76c1a39 + 04c1ee4 commit 5947568

File tree

2 files changed

+56
-34
lines changed

2 files changed

+56
-34
lines changed

src/decoder.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,30 @@ const isJsonArray = (json: any): boolean => json instanceof Array;
5656
const isJsonObject = (json: any): boolean =>
5757
typeof json === 'object' && json !== null && !isJsonArray(json);
5858

59-
const expectedGot = (expected: string, got: any) =>
60-
`expected ${expected}, got ${JSON.stringify(got)}`;
59+
const typeString = (json: any): string => {
60+
switch (typeof json) {
61+
case 'string':
62+
return 'a string';
63+
case 'number':
64+
return 'a number';
65+
case 'boolean':
66+
return 'a boolean';
67+
case 'undefined':
68+
return 'undefined';
69+
case 'object':
70+
if (json instanceof Array) {
71+
return 'an array';
72+
} else if (json === null) {
73+
return 'null';
74+
} else {
75+
return 'an object';
76+
}
77+
default:
78+
return JSON.stringify(json);
79+
}
80+
};
81+
82+
const expectedGot = (expected: string, got: any) => `expected ${expected}, got ${typeString(got)}`;
6183

6284
const printPath = (paths: (string | number)[]): string =>
6385
paths.map(path => (typeof path === 'string' ? `.${path}` : `[${path}]`)).join('');
@@ -214,7 +236,7 @@ export class Decoder<A> {
214236
(json: any) =>
215237
json === value
216238
? Result.ok(value)
217-
: Result.err({message: expectedGot(JSON.stringify(value), json)})
239+
: Result.err({message: `expected ${JSON.stringify(value)}, got ${JSON.stringify(json)}`})
218240
);
219241
}
220242

test/json-decode.test.ts

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('string', () => {
3030
it('fails when given a number', () => {
3131
expect(decoder.run(1)).toMatchObject({
3232
ok: false,
33-
error: {at: 'input', message: 'expected a string, got 1'}
33+
error: {at: 'input', message: 'expected a string, got a number'}
3434
});
3535
});
3636

@@ -44,7 +44,7 @@ describe('string', () => {
4444
it('fails when given a boolean', () => {
4545
expect(decoder.run(true)).toMatchObject({
4646
ok: false,
47-
error: {at: 'input', message: 'expected a string, got true'}
47+
error: {at: 'input', message: 'expected a string, got a boolean'}
4848
});
4949
});
5050
});
@@ -59,14 +59,14 @@ describe('number', () => {
5959
it('fails when given a string', () => {
6060
expect(decoder.run('hey')).toMatchObject({
6161
ok: false,
62-
error: {at: 'input', message: 'expected a number, got "hey"'}
62+
error: {at: 'input', message: 'expected a number, got a string'}
6363
});
6464
});
6565

6666
it('fails when given boolean', () => {
6767
expect(decoder.run(true)).toMatchObject({
6868
ok: false,
69-
error: {at: 'input', message: 'expected a number, got true'}
69+
error: {at: 'input', message: 'expected a number, got a boolean'}
7070
});
7171
});
7272
});
@@ -81,14 +81,14 @@ describe('boolean', () => {
8181
it('fails when given a string', () => {
8282
expect(decoder.run('hey')).toMatchObject({
8383
ok: false,
84-
error: {at: 'input', message: 'expected a boolean, got "hey"'}
84+
error: {at: 'input', message: 'expected a boolean, got a string'}
8585
});
8686
});
8787

8888
it('fails when given a number', () => {
8989
expect(decoder.run(1)).toMatchObject({
9090
ok: false,
91-
error: {at: 'input', message: 'expected a boolean, got 1'}
91+
error: {at: 'input', message: 'expected a boolean, got a number'}
9292
});
9393
});
9494
});
@@ -173,7 +173,7 @@ describe('object', () => {
173173

174174
expect(decoder.run('true')).toMatchObject({
175175
ok: false,
176-
error: {at: 'input', message: 'expected an object, got "true"'}
176+
error: {at: 'input', message: 'expected an object, got a string'}
177177
});
178178
});
179179

@@ -182,7 +182,7 @@ describe('object', () => {
182182

183183
expect(decoder.run([])).toMatchObject({
184184
ok: false,
185-
error: {at: 'input', message: 'expected an object, got []'}
185+
error: {at: 'input', message: 'expected an object, got an array'}
186186
});
187187
});
188188

@@ -200,7 +200,7 @@ describe('object', () => {
200200

201201
expect(decoder.run({name: 5})).toMatchObject({
202202
ok: false,
203-
error: {at: 'input.name', message: 'expected a string, got 5'}
203+
error: {at: 'input.name', message: 'expected a string, got a number'}
204204
});
205205
});
206206

@@ -216,7 +216,7 @@ describe('object', () => {
216216
const error = decoder.run({hello: {hey: {'Howdy!': {}}}});
217217
expect(error).toMatchObject({
218218
ok: false,
219-
error: {at: 'input.hello.hey.Howdy!', message: 'expected a string, got {}'}
219+
error: {at: 'input.hello.hey.Howdy!', message: 'expected a string, got an object'}
220220
});
221221
});
222222
});
@@ -264,15 +264,15 @@ describe('array', () => {
264264
it('fails when given something other than a array', () => {
265265
expect(decoder.run('oops')).toMatchObject({
266266
ok: false,
267-
error: {at: 'input', message: 'expected an array, got "oops"'}
267+
error: {at: 'input', message: 'expected an array, got a string'}
268268
});
269269
});
270270

271271
describe('when given something other than an array', () => {
272272
it('fails when the elements are of the wrong type', () => {
273273
expect(decoder.run(['dang'])).toMatchObject({
274274
ok: false,
275-
error: {at: 'input[0]', message: 'expected a number, got "dang"'}
275+
error: {at: 'input[0]', message: 'expected a number, got a string'}
276276
});
277277
});
278278

@@ -281,7 +281,7 @@ describe('array', () => {
281281

282282
expect(nestedDecoder.run([[], [], [[1, 2, 3, false]]])).toMatchObject({
283283
ok: false,
284-
error: {at: 'input[2][0][3]', message: 'expected a number, got false'}
284+
error: {at: 'input[2][0][3]', message: 'expected a number, got a boolean'}
285285
});
286286
});
287287
});
@@ -302,21 +302,21 @@ describe('dict', () => {
302302
it('fails if a value cannot be decoded', () => {
303303
expect(decoder.run({oh: 'no'})).toMatchObject({
304304
ok: false,
305-
error: {at: 'input.oh', message: 'expected a number, got "no"'}
305+
error: {at: 'input.oh', message: 'expected a number, got a string'}
306306
});
307307
});
308308

309309
it('fails if given an array', () => {
310310
expect(decoder.run([])).toMatchObject({
311311
ok: false,
312-
error: {at: 'input', message: 'expected an object, got []'}
312+
error: {at: 'input', message: 'expected an object, got an array'}
313313
});
314314
});
315315

316316
it('fails if given a primitive', () => {
317317
expect(decoder.run(5)).toMatchObject({
318318
ok: false,
319-
error: {at: 'input', message: 'expected an object, got 5'}
319+
error: {at: 'input', message: 'expected an object, got a number'}
320320
});
321321
});
322322
});
@@ -369,7 +369,7 @@ describe('optional', () => {
369369
it('fails when the value is invalid', () => {
370370
expect(decoder.run(false)).toMatchObject({
371371
ok: false,
372-
error: {at: 'input', message: 'expected a number, got false'}
372+
error: {at: 'input', message: 'expected a number, got a boolean'}
373373
});
374374
});
375375
});
@@ -397,7 +397,7 @@ describe('optional', () => {
397397
const error = decoder.run({id: 3, isDog: 'supdog'});
398398
expect(error).toMatchObject({
399399
ok: false,
400-
error: {at: 'input.isDog', message: 'expected a boolean, got "supdog"'}
400+
error: {at: 'input.isDog', message: 'expected a boolean, got a string'}
401401
});
402402
});
403403
});
@@ -427,7 +427,7 @@ describe('oneOf', () => {
427427
at: 'input',
428428
message:
429429
'expected a value matching one of the decoders, got the errors ' +
430-
'["at error: expected a string, got []", "at error: expected a number, got []"]'
430+
'["at error: expected a string, got an array", "at error: expected a number, got an array"]'
431431
}
432432
});
433433
});
@@ -443,7 +443,7 @@ describe('oneOf', () => {
443443
at: 'input[0]',
444444
message:
445445
'expected a value matching one of the decoders, got the errors ' +
446-
'["at error[1].a.b: expected a number, got true", ' +
446+
'["at error[1].a.b: expected a number, got a boolean", ' +
447447
'"at error[1].a.x: path does not exist"]'
448448
}
449449
});
@@ -487,7 +487,7 @@ describe('union', () => {
487487
at: 'input',
488488
message:
489489
'expected a value matching one of the decoders, got the errors ' +
490-
'["at error.kind: expected "a", got "b"", "at error.value: expected a boolean, got 12"]'
490+
'["at error.kind: expected "a", got "b"", "at error.value: expected a boolean, got a number"]'
491491
}
492492
});
493493
});
@@ -542,7 +542,7 @@ describe('valueAt', () => {
542542
it('fails when the decoder fails at the end of the path', () => {
543543
expect(decoder.run({a: ['a', {b: 12}]})).toMatchObject({
544544
ok: false,
545-
error: {at: 'input.a[1].b', message: 'expected a string, got 12'}
545+
error: {at: 'input.a[1].b', message: 'expected a string, got a number'}
546546
});
547547
});
548548
});
@@ -569,11 +569,11 @@ describe('valueAt', () => {
569569

570570
expect(decoder.run('abc')).toMatchObject({
571571
ok: false,
572-
error: {at: 'input.a', message: 'expected an object, got "abc"'}
572+
error: {at: 'input.a', message: 'expected an object, got a string'}
573573
});
574574
expect(decoder.run(true)).toMatchObject({
575575
ok: false,
576-
error: {at: 'input.a', message: 'expected an object, got true'}
576+
error: {at: 'input.a', message: 'expected an object, got a boolean'}
577577
});
578578
});
579579

@@ -583,7 +583,7 @@ describe('valueAt', () => {
583583
const error = decoder.run({a: {b: 1}});
584584
expect(error).toMatchObject({
585585
ok: false,
586-
error: {at: 'input.a.b.c', message: 'expected an object, got 1'}
586+
error: {at: 'input.a.b.c', message: 'expected an object, got a number'}
587587
});
588588
});
589589

@@ -593,7 +593,7 @@ describe('valueAt', () => {
593593
const error = decoder.run([[false]]);
594594
expect(error).toMatchObject({
595595
ok: false,
596-
error: {at: 'input[0][0][1]', message: 'expected an array, got false'}
596+
error: {at: 'input[0][0][1]', message: 'expected an array, got a boolean'}
597597
});
598598
});
599599
});
@@ -638,7 +638,7 @@ describe('lazy', () => {
638638
it('does not alter the error message', () => {
639639
expect(decoder.run(5)).toMatchObject({
640640
ok: false,
641-
error: {at: 'input', message: 'expected a string, got 5'}
641+
error: {at: 'input', message: 'expected a string, got a number'}
642642
});
643643
});
644644
});
@@ -668,7 +668,7 @@ describe('lazy', () => {
668668

669669
expect(decoder.run(badTree)).toMatchObject({
670670
ok: false,
671-
error: {at: 'input.replies[0].replies[0]', message: 'expected an object, got "hello"'}
671+
error: {at: 'input.replies[0].replies[0]', message: 'expected an object, got a string'}
672672
});
673673
});
674674
});
@@ -686,7 +686,7 @@ describe('runPromise', () => {
686686
kind: 'DecoderError',
687687
input: 42,
688688
at: 'input',
689-
message: 'expected a boolean, got 42'
689+
message: 'expected a boolean, got a number'
690690
});
691691
});
692692

@@ -704,7 +704,7 @@ describe('runWithException', () => {
704704

705705
it('throws an exception when the decoder fails', () => {
706706
expect(() => decoder.runWithException(42)).toThrowError(
707-
'Input: 42\nFailed at input: expected a boolean, got 42'
707+
'Input: 42\nFailed at input: expected a boolean, got a number'
708708
);
709709
});
710710
});
@@ -763,7 +763,7 @@ describe('andThen', () => {
763763
const json = {version: 3, a: 1};
764764
expect(decoder.run(json)).toMatchObject({
765765
ok: false,
766-
error: {at: 'input.a', message: 'expected a boolean, got 1'}
766+
error: {at: 'input.a', message: 'expected a boolean, got a number'}
767767
});
768768
});
769769
});

0 commit comments

Comments
 (0)