Skip to content

Commit 525f078

Browse files
authored
Merge pull request #64 from mitko-slapdash/mitko/showFoundInput
Append input found to error message in TypeGuardError
2 parents 9f6058e + 183e7e1 commit 525f078

File tree

10 files changed

+55
-39
lines changed

10 files changed

+55
-39
lines changed

index.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export function ValidateClass(errorConstructor?: { new(): Error }): <TFunction e
167167
export class TypeGuardError extends Error {
168168
public readonly path: string[];
169169
public readonly reason: Reason;
170+
public readonly input: unknown;
170171
}
171172

172173
interface ExpectedString {

index.js

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,27 @@ function checkGetErrorObject(getErrorObject) {
88

99
const assertionsMetadataKey = Symbol('assertions');
1010

11+
function inputObjectAtPath(path, inputObject) {
12+
let subField = inputObject;
13+
for (const key of path.slice(1)) {
14+
subField = subField[
15+
key.startsWith("[") ? parseInt(key.replace("[", "").replace("]", "")) : key
16+
];
17+
}
18+
return subField;
19+
}
20+
21+
function appendInputToErrorMessage(message, path, inputObject) {
22+
return message + ', found: ' + JSON.stringify(inputObjectAtPath(path, inputObject));
23+
}
24+
1125
class TypeGuardError extends Error {
12-
constructor(errorObject) {
13-
super(errorObject.message);
26+
constructor(errorObject, inputObject) {
27+
super(appendInputToErrorMessage(errorObject.message, errorObject.path, inputObject));
1428
this.name = 'TypeGuardError';
1529
this.path = errorObject.path;
1630
this.reason = errorObject.reason;
31+
this.input = inputObject
1732
}
1833
}
1934

@@ -37,7 +52,7 @@ function ValidateClass(errorConstructor = TypeGuardError) {
3752
for (let i = 0; i < assertions.length; i++) {
3853
const errorObject = assertions[i].assertion(args[i]);
3954
if (errorObject !== null) {
40-
throw new errorConstructor(errorObject);
55+
throw new errorConstructor(errorObject, args[i]);
4156
}
4257
}
4358
return originalMethod.apply(this, args);
@@ -59,7 +74,7 @@ function assertType(obj, getErrorObject = defaultGetErrorObject) {
5974
if (errorObject === null) {
6075
return obj;
6176
} else {
62-
throw new TypeGuardError(errorObject);
77+
throw new TypeGuardError(errorObject, obj);
6378
}
6479
}
6580

src/transform-inline/transform-node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function transformDecorator(node: ts.Decorator, parameterType: ts.Type, paramete
7676

7777
/** Figures out an appropriate human-readable name for the variable designated by `node`. */
7878
function extractVariableName(node: ts.Node | undefined) {
79-
return node != null && ts.isIdentifier(node) ? node.escapedText.toString() : '$';
79+
return node !== undefined && ts.isIdentifier(node) ? node.escapedText.toString() : '$';
8080
}
8181

8282
export function transformNode(node: ts.Node, visitorContext: PartialVisitorContext): ts.Node {

test/case-10.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as assert from 'assert';
2-
import { createIs, createAssertType } from '../index';
2+
import { createAssertType, createIs } from '../index';
33

44
describe('createIs', () => {
55
describe('createIs<number>', () => {
@@ -34,7 +34,7 @@ describe('createIs', () => {
3434

3535
describe('createAssertType', () => {
3636
describe('createAssertType<number>', () => {
37-
const expectedMessageRegExp = /validation failed at \$: expected a number$/;
37+
const expectedMessageRegExp = /validation failed at \$: expected a number, found: .*$/;
3838
const assertNumber = createAssertType<number>();
3939

4040
it('should return a function', () => {

test/case-11.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('@ValidateClass, @AssertType', () => {
2424
});
2525

2626
it('should throw an error for non-numbers', () => {
27-
const expectedMessageRegExp = /validation failed at parameter: expected a number$/;
27+
const expectedMessageRegExp = /validation failed at parameter: expected a number, found: .*$/;
2828
assert.throws(() => instance.testMethod('' as any), expectedMessageRegExp);
2929
assert.throws(() => instance.testMethod('0' as any), expectedMessageRegExp);
3030
assert.throws(() => instance.testMethod('1' as any), expectedMessageRegExp);
@@ -52,7 +52,7 @@ describe('@ValidateClass, @AssertType', () => {
5252
});
5353

5454
it('should throw an error for non-strings', () => {
55-
const expectedMessageRegExp = /validation failed at parameter: expected a string$/;
55+
const expectedMessageRegExp = /validation failed at parameter: expected a string, found: .*$/;
5656
assert.throws(() => instance.testMethod(0 as any), expectedMessageRegExp);
5757
assert.throws(() => instance.testMethod(1 as any), expectedMessageRegExp);
5858
assert.throws(() => instance.testMethod(true as any), expectedMessageRegExp);

test/case-15.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ describe('@ValidateClass, @AssertType', () => {
2929
});
3030

3131
it('should throw an error for non-numbers', () => {
32-
const expectedMessageRegExp = /validation failed at parameter: expected a number$/;
32+
const expectedMessageRegExp = /validation failed at parameter: expected a number, found: .*$/;
3333
assert.throws(() => instance.testMethod('' as any), expectedMessageRegExp);
3434
assert.throws(() => instance.testMethod('0' as any), expectedMessageRegExp);
3535
assert.throws(() => instance.testMethod('1' as any), expectedMessageRegExp);
@@ -67,7 +67,7 @@ describe('@ValidateClass, @AssertType', () => {
6767
});
6868

6969
it('should throw an error for non-numbers', () => {
70-
const expectedMessageRegExp = /validation failed at parameter: expected a number$/;
70+
const expectedMessageRegExp = /validation failed at parameter: expected a number, found: .*$/;
7171
assert.throws(() => instance.testMethod('' as any), expectedMessageRegExp);
7272
assert.throws(() => instance.testMethod('0' as any), expectedMessageRegExp);
7373
assert.throws(() => instance.testMethod('1' as any), expectedMessageRegExp);

test/case-9.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { assertType } from '../index';
33

44
describe('assertType', () => {
55
describe('assertType<number>', () => {
6-
const expectedMessageRegExp = /validation failed at \$: expected a number$/;
6+
const expectedMessageRegExp = /validation failed at \$: expected a number, found: .*$/;
77

88
it('should return the numbers passed to it', () => {
99
assert.deepStrictEqual(assertType<number>(-1), -1);
@@ -21,7 +21,7 @@ describe('assertType', () => {
2121
assert.throws(() => assertType<number>(true), expectedMessageRegExp);
2222
assert.throws(() => assertType<number>(false), expectedMessageRegExp);
2323
assert.throws(() => assertType<number>(null), expectedMessageRegExp);
24-
assert.throws(() => assertType<number>(undefined), /validation failed at undefined: expected a number$/);
24+
assert.throws(() => assertType<number>(undefined), /validation failed at undefined: expected a number, found: .*$/);
2525
});
2626
});
2727
});

test/issue-12.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as assert from 'assert';
2-
import { is, assertType } from '../index';
2+
import { assertType, is } from '../index';
33

44
/* https://github.com/woutervh-/typescript-is/issues/12 */
55

@@ -35,12 +35,12 @@ describe('is', () => {
3535

3636
describe('assertType<ConfigInit>', () => {
3737
it('should throw an error when invalid objects are passed to it', () => {
38-
const expectedMessageRegExp1 = /validation failed at \$: expected an object$/;
39-
const expectedMessageRegExp2 = /validation failed at \$: expected 'folder' in object$/;
40-
const expectedMessageRegExp3 = /validation failed at \$\.children: expected an array$/;
41-
const expectedMessageRegExp4 = /validation failed at \$\.children\.\[0\]: expected an object$/;
42-
const expectedMessageRegExp5 = /validation failed at \$\.children\.\[0\]: expected 'folder' in object$/;
43-
const expectedMessageRegExp6 = /validation failed at \$\.children\.\[0\]\.children: expected an array$/;
38+
const expectedMessageRegExp1 = /validation failed at \$: expected an object, found: .*$/;
39+
const expectedMessageRegExp2 = /validation failed at \$: expected 'folder' in object, found: .*$/;
40+
const expectedMessageRegExp3 = /validation failed at \$\.children: expected an array, found: .*$/;
41+
const expectedMessageRegExp4 = /validation failed at \$\.children\.\[0\]: expected an object, found: .*$/;
42+
const expectedMessageRegExp5 = /validation failed at \$\.children\.\[0\]: expected 'folder' in object, found: .*$/;
43+
const expectedMessageRegExp6 = /validation failed at \$\.children\.\[0\]\.children: expected an array, found: .*$/;
4444
assert.throws(() => assertType<ConfigInit>(null), expectedMessageRegExp1);
4545
assert.throws(() => assertType<ConfigInit>({}), expectedMessageRegExp2);
4646
assert.throws(() => assertType<ConfigInit>({ folder: '.', children: 'foo' }), expectedMessageRegExp3);

test/issue-2.ts

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@ describe('assertType', () => {
1313
});
1414

1515
it('should throw an error if invalid objects are passed to it', () => {
16-
const expectedMessageRegExp = /validation failed at \$: expected an object$/;
16+
const expectedMessageRegExp = /validation failed at \$: expected an object, found: .*$/;
1717
assert.throws(() => assertType<{ foo: string }>(0), expectedMessageRegExp);
1818
assert.throws(() => assertType<{ foo: string }>([]), expectedMessageRegExp);
1919
assert.throws(() => assertType<{ foo: string }>(null), expectedMessageRegExp);
2020
assert.throws(() => assertType<{ foo: string }>(true), expectedMessageRegExp);
2121
});
2222

2323
it('should throw an error if objects without foo are passed to it', () => {
24-
const expectedMessageRegExp = /validation failed at \$: expected 'foo' in object$/;
24+
const expectedMessageRegExp = /validation failed at \$: expected 'foo' in object, found: .*$/;
2525
assert.throws(() => assertType<{ foo: string }>({}), expectedMessageRegExp);
2626
assert.throws(() => assertType<{ foo: string }>({ bar: 'baz' }), expectedMessageRegExp);
2727
});
2828

2929
it('should throw an error if objects with foo not a string are passed to it', () => {
30-
const expectedMessageRegExp = /validation failed at \$\.foo: expected a string$/;
30+
const expectedMessageRegExp = /validation failed at \$\.foo: expected a string, found: .*$/;
3131
assert.throws(() => assertType<{ foo: string }>({ foo: 0 }), expectedMessageRegExp);
3232
assert.throws(() => assertType<{ foo: string }>({ foo: false }), expectedMessageRegExp);
3333
});
@@ -43,21 +43,21 @@ describe('assertType', () => {
4343
});
4444

4545
it('should throw an error if objects without foo are passed to it', () => {
46-
const expectedMessageRegExp = /validation failed at \$: expected 'foo' in object$/;
46+
const expectedMessageRegExp = /validation failed at \$: expected 'foo' in object, found: .*$/;
4747
assert.throws(() => assertType<{ foo: string }>({}), expectedMessageRegExp);
4848
assert.throws(() => assertType<{ foo: string }>({ bar: 'baz' }), expectedMessageRegExp);
4949
});
5050

5151
it('should throw an error if invalid objects are passed to it', () => {
52-
const expectedMessageRegExp = /validation failed at \$: expected an object$/;
52+
const expectedMessageRegExp = /validation failed at \$: expected an object, found: .*$/;
5353
assert.throws(() => assertType<{ foo: number[] }>(0), expectedMessageRegExp);
5454
assert.throws(() => assertType<{ foo: number[] }>(null), expectedMessageRegExp);
5555
assert.throws(() => assertType<{ foo: number[] }>(true), expectedMessageRegExp);
5656
});
5757

5858
it('should throw an error if objects where foo is not an array of numbers are passed to it', () => {
59-
const expectedMessageRegExp1 = /validation failed at \$\.foo\.\[1\]: expected a number$/;
60-
const expectedMessageRegExp2 = /validation failed at \$\.foo\.\[0\]: expected a number$/;
59+
const expectedMessageRegExp1 = /validation failed at \$\.foo\.\[1\]: expected a number, found: .*$/;
60+
const expectedMessageRegExp2 = /validation failed at \$\.foo\.\[0\]: expected a number, found: .*$/;
6161
assert.throws(() => assertType<{ foo: number[] }>({ foo: [0, '0'] }), expectedMessageRegExp1);
6262
assert.throws(() => assertType<{ foo: number[] }>({ foo: ['1'] }), expectedMessageRegExp2);
6363
assert.throws(() => assertType<{ foo: number[] }>({ foo: [{}] }), expectedMessageRegExp2);
@@ -77,35 +77,35 @@ describe('assertType', () => {
7777
});
7878

7979
it('should throw an error if nested objects with foo not \'bar\' or \'baz\' are passed to it', () => {
80-
const expectedMessageRegExp = /validation failed at \$\.nested\.foo: there are no valid alternatives$/;
80+
const expectedMessageRegExp = /validation failed at \$\.nested\.foo: there are no valid alternatives, found: .*$/;
8181
assert.throws(() => assertType<{ nested: Nested }>({ nested: { foo: 'qux' } }), expectedMessageRegExp);
8282
assert.throws(() => assertType<{ nested: Nested }>({ nested: { foo: 0 } }), expectedMessageRegExp);
8383
assert.throws(() => assertType<{ nested: Nested }>({ nested: { foo: [] } }), expectedMessageRegExp);
8484
assert.throws(() => assertType<{ nested: Nested }>({ nested: { foo: {} } }), expectedMessageRegExp);
8585
});
8686

8787
it('should throw an error if nested objects without foo are passed to it', () => {
88-
const expectedMessageRegExp = /validation failed at \$\.nested: expected 'foo' in object$/;
88+
const expectedMessageRegExp = /validation failed at \$\.nested: expected 'foo' in object, found: .*$/;
8989
assert.throws(() => assertType<{ nested: Nested }>({ nested: {} }), expectedMessageRegExp);
9090
assert.throws(() => assertType<{ nested: Nested }>({ nested: { foh: 'bar' } }), expectedMessageRegExp);
9191
});
9292

9393
it('should throw an error if nested properties that are not objects are passed to it', () => {
94-
const expectedMessageRegExp = /validation failed at \$\.nested: expected an object$/;
94+
const expectedMessageRegExp = /validation failed at \$\.nested: expected an object, found: .*$/;
9595
assert.throws(() => assertType<{ nested: Nested }>({ nested: 0 }), expectedMessageRegExp);
9696
assert.throws(() => assertType<{ nested: Nested }>({ nested: true }), expectedMessageRegExp);
9797
assert.throws(() => assertType<{ nested: Nested }>({ nested: null }), expectedMessageRegExp);
9898
assert.throws(() => assertType<{ nested: Nested }>({ nested: [] }), expectedMessageRegExp);
9999
});
100100

101101
it('should throw an error if objects without nested are passed to it', () => {
102-
const expectedMessageRegExp = /validation failed at \$: expected 'nested' in object$/;
102+
const expectedMessageRegExp = /validation failed at \$: expected 'nested' in object, found: .*$/;
103103
assert.throws(() => assertType<{ nested: Nested }>({ nisted: { foo: 'bar' } }), expectedMessageRegExp);
104104
assert.throws(() => assertType<{ nested: Nested }>({ nisted: { foh: 'baz' } }), expectedMessageRegExp);
105105
});
106106

107107
it('should throw an error if other objects are passed to it', () => {
108-
const expectedMessageRegExp = /validation failed at \$: expected an object$/;
108+
const expectedMessageRegExp = /validation failed at \$: expected an object, found: .*$/;
109109
assert.throws(() => assertType<{ nested: Nested }>('0'), expectedMessageRegExp);
110110
assert.throws(() => assertType<{ nested: Nested }>(1), expectedMessageRegExp);
111111
assert.throws(() => assertType<{ nested: Nested }>([]), expectedMessageRegExp);
@@ -122,8 +122,8 @@ describe('assertType', () => {
122122
});
123123

124124
it('should throw an error if objects with non-boolen values are passed to it', () => {
125-
const expectedMessageRegExp1 = /validation failed at \$\.foo: expected a boolean$/;
126-
const expectedMessageRegExp2 = /validation failed at \$\.bar: expected a boolean$/;
125+
const expectedMessageRegExp1 = /validation failed at \$\.foo: expected a boolean, found: .*$/;
126+
const expectedMessageRegExp2 = /validation failed at \$\.bar: expected a boolean, found: .*$/;
127127
assert.throws(() => assertType<{ [Key: string]: boolean }>({ foo: 0 }), expectedMessageRegExp1);
128128
assert.throws(() => assertType<{ [Key: string]: boolean }>({ bar: 'foo' }), expectedMessageRegExp2);
129129
assert.throws(() => assertType<{ [Key: string]: boolean }>({ bar: [] }), expectedMessageRegExp2);

test/issue-22.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as assert from 'assert';
2-
import { equals, createEquals, assertEquals, createAssertEquals } from '../index';
2+
import { assertEquals, createAssertEquals, createEquals, equals } from '../index';
33

44
/* https://github.com/woutervh-/typescript-is/issues/22 */
55

@@ -61,13 +61,13 @@ describe('assertEquals', () => {
6161
});
6262

6363
it('should throw an error if objects without `foo` being a number are passed to it', () => {
64-
const expectedMessageRegExp = /validation failed at \$: expected 'foo' in object$/;
64+
const expectedMessageRegExp = /validation failed at \$: expected 'foo' in object, found: .*$/;
6565
assert.throws(() => assertEquals<{ foo: number }>({}), expectedMessageRegExp);
6666
assert.throws(() => assertEquals<{ foo: number }>({ bar: 0 }), expectedMessageRegExp);
6767
});
6868

6969
it('should throw an error if objects with `foo` being a number and with other properties are passed to it', () => {
70-
const expectedMessageRegExp = /validation failed at \$: superfluous property 'bar' in object$/;
70+
const expectedMessageRegExp = /validation failed at \$: superfluous property 'bar' in object, found: .*$/;
7171
assert.throws(() => assertEquals<{ foo: number }>({ foo: 0, bar: 1 }), expectedMessageRegExp);
7272
assert.throws(() => assertEquals<{ foo: number }>({ foo: 0, bar: 'value' }), expectedMessageRegExp);
7373
});
@@ -88,13 +88,13 @@ describe('createAssertEquals', () => {
8888
});
8989

9090
it('should throw an error if objects without `foo` being a number are passed to it', () => {
91-
const expectedMessageRegExp = /validation failed at \$: expected 'foo' in object$/;
91+
const expectedMessageRegExp = /validation failed at \$: expected 'foo' in object, found: .*$/;
9292
assert.throws(() => assertObject({}), expectedMessageRegExp);
9393
assert.throws(() => assertObject({ bar: 0 }), expectedMessageRegExp);
9494
});
9595

9696
it('should throw an error if objects with `foo` being a number and with other properties are passed to it', () => {
97-
const expectedMessageRegExp = /validation failed at \$: superfluous property 'bar' in object$/;
97+
const expectedMessageRegExp = /validation failed at \$: superfluous property 'bar' in object, found: .*$/;
9898
assert.throws(() => assertObject({ foo: 0, bar: 1 }), expectedMessageRegExp);
9999
assert.throws(() => assertObject({ foo: 0, bar: 'value' }), expectedMessageRegExp);
100100
});

0 commit comments

Comments
 (0)