Skip to content

Commit d7e9ade

Browse files
committed
Improved error messages for class-validator
1 parent 0f28135 commit d7e9ade

File tree

6 files changed

+167
-54
lines changed

6 files changed

+167
-54
lines changed

.changeset/lovely-bobcats-hang.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@typeschema/class-validator': minor
3+
'@typeschema/main': patch
4+
'@typeschema/all': patch
5+
---
6+
7+
Improved error messages for class-validator

README.md

+18-18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/all/src/__tests__/class-validator.test.ts

+57-4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/class-validator/src/__tests__/class-validator.test.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,13 @@ describe('class-validator', () => {
4444
@IsDateString()
4545
updatedAt!: string;
4646

47+
@IsOptional()
4748
@ValidateNested()
48-
nested!: NestedSchema;
49+
nested?: NestedSchema;
50+
4951
@IsOptional()
5052
@ValidateNested()
51-
nestedArray?: NestedSchema[];
53+
nestedArray?: Array<NestedSchema>;
5254
}
5355
const schema = Schema;
5456

@@ -67,7 +69,7 @@ describe('class-validator', () => {
6769
6870
id: 'c4a760a8-dbcf-4e14-9f39-645a8e933d74',
6971
name: 'John Doe',
70-
updatedAt: '2021-01-01T00:00:00.000Z'
72+
updatedAt: '2021-01-01T00:00:00.000Z',
7173
};
7274

7375
const badNestedData = {
@@ -100,7 +102,7 @@ describe('class-validator', () => {
100102
{
101103
message: 'age must be an integer number',
102104
path: ['age'],
103-
}
105+
},
104106
],
105107
success: false,
106108
});
@@ -109,20 +111,20 @@ describe('class-validator', () => {
109111
issues: [
110112
{
111113
message: 'value should not be empty',
112-
path: ['nested.value'],
114+
path: ['nested', 'value'],
113115
},
114116
{
115117
message: 'value must be a string',
116-
path: ['nested.value'],
118+
path: ['nested', 'value'],
117119
},
118120
{
119121
message: 'value should not be empty',
120-
path: ['nestedArray[0].value'],
122+
path: ['nestedArray', 0, 'value'],
121123
},
122124
{
123125
message: 'value must be a string',
124-
path: ['nestedArray[0].value'],
125-
}
126+
path: ['nestedArray', 0, 'value'],
127+
},
126128
],
127129
success: false,
128130
});

packages/class-validator/src/validation.ts

+17-19
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,34 @@
1-
/* eslint-disable prettier/prettier */
21
import type {AdapterResolver} from './resolver';
32
import type {ValidationAdapter, ValidationIssue} from '@typeschema/core';
43

54
import {memoize} from '@typeschema/core';
6-
import { ValidationError } from "class-validator";
5+
import {ValidationError} from 'class-validator';
76

87
const importValidationModule = memoize(async () => {
98
const {validate} = await import('class-validator');
109
return {validate};
1110
});
1211

12+
function getIssues(
13+
error: ValidationError,
14+
parentPath: Array<PropertyKey>,
15+
): Array<ValidationIssue> {
16+
const path = [
17+
...parentPath,
18+
Number.isInteger(+error.property) ? +error.property : error.property,
19+
];
20+
return Object.values(error.constraints ?? {})
21+
.map((message): ValidationIssue => ({message, path}))
22+
.concat(
23+
error.children?.flatMap(childError => getIssues(childError, path)) ?? [],
24+
);
25+
}
26+
1327
export const validationAdapter: ValidationAdapter<
1428
AdapterResolver
1529
> = async schema => {
1630
const {validate} = await importValidationModule();
1731
return async data => {
18-
function getIssues(error: ValidationError, parentPath = ""): ValidationIssue[] {
19-
const currentPath = parentPath
20-
? Number.isInteger(+error.property) ? `${parentPath}[${error.property}]` : `${parentPath}.${error.property}`
21-
: error.property;
22-
const constraints = error.constraints ? Object.values(error.constraints) : [];
23-
const childIssues = error.children ? error.children.flatMap(childError => getIssues(childError, currentPath)) : [];
24-
25-
return [
26-
...constraints.map((message) => ({
27-
message: message,
28-
path: [currentPath],
29-
})),
30-
...childIssues
31-
];
32-
}
33-
3432
const errors = await validate(Object.assign(new schema(), data));
3533
if (errors.length === 0) {
3634
return {
@@ -40,7 +38,7 @@ export const validationAdapter: ValidationAdapter<
4038
};
4139
}
4240
return {
43-
issues: errors.flatMap(error => getIssues(error)),
41+
issues: errors.flatMap(error => getIssues(error, [])),
4442
success: false,
4543
};
4644
};

0 commit comments

Comments
 (0)