Skip to content

Commit db389b9

Browse files
authored
feat(ArrayType): enhance nested array validation (#82)
* feat(array): enhance nested array validation - Add support for array index in nested object validation - Improve getFieldType to handle array paths - Add comprehensive tests for nested array validation (max 3 levels) - Add arrayTypeSchemaSpec for better type handling - Improve error handling in nested structures * fix: Support nested array validation in ArrayType - Add isArrayTypeNested flag to handle nested array validation - Skip array type check for nested array elements - Update test cases for nested array validation * Update Schema.ts
1 parent eef416a commit db389b9

File tree

7 files changed

+740
-179
lines changed

7 files changed

+740
-179
lines changed

package-lock.json

Lines changed: 3 additions & 91 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@
4040
],
4141
"homepage": "https://github.com/rsuite/schema-typed#readme",
4242
"dependencies": {
43-
"get-value": "^3.0.1",
44-
"set-value": "^4.1.0"
43+
"lodash": "^4.17.21"
4544
},
4645
"devDependencies": {
4746
"@istanbuljs/nyc-config-typescript": "^1.0.2",

src/ArrayType.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MixedType } from './MixedType';
1+
import { MixedType, arrayTypeSchemaSpec } from './MixedType';
22
import { PlainObject, CheckResult, ErrorMessageType } from './types';
33
import { ArrayTypeLocale } from './locales';
44

@@ -8,10 +8,19 @@ export class ArrayType<DataType = any, E = ErrorMessageType> extends MixedType<
88
E,
99
ArrayTypeLocale
1010
> {
11+
[arrayTypeSchemaSpec]: MixedType<any, DataType, E>;
12+
private isArrayTypeNested = false;
13+
1114
constructor(errorMessage?: E | string) {
1215
super('array');
1316
super.pushRule({
14-
onValid: v => Array.isArray(v),
17+
onValid: v => {
18+
// Skip array type check for nested array elements
19+
if (this.isArrayTypeNested) {
20+
return true;
21+
}
22+
return Array.isArray(v);
23+
},
1524
errorMessage: errorMessage || this.locale.type
1625
});
1726
}
@@ -67,8 +76,28 @@ export class ArrayType<DataType = any, E = ErrorMessageType> extends MixedType<
6776
}
6877

6978
of(type: MixedType<any, DataType, E>) {
79+
this[arrayTypeSchemaSpec] = type;
80+
81+
// Mark inner ArrayType as nested when dealing with nested arrays
82+
if (type instanceof ArrayType) {
83+
type.isArrayTypeNested = true;
84+
}
85+
7086
super.pushRule({
7187
onValid: (items, data, fieldName) => {
88+
// For non-array values in nested arrays, pass directly to inner type validation
89+
if (!Array.isArray(items) && this.isArrayTypeNested) {
90+
return type.check(items, data, fieldName);
91+
}
92+
93+
// For non-array values in non-nested arrays, return array type error
94+
if (!Array.isArray(items)) {
95+
return {
96+
hasError: true,
97+
errorMessage: this.locale.type
98+
};
99+
}
100+
72101
const checkResults = items.map((value, index) => {
73102
const name = Array.isArray(fieldName)
74103
? [...fieldName, `[${index}]`]

src/MixedType.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,38 @@ type ProxyOptions = {
2626
};
2727

2828
export const schemaSpecKey = 'objectTypeSchemaSpec';
29+
export const arrayTypeSchemaSpec = 'arrayTypeSchemaSpec';
2930

3031
/**
3132
* Get the field type from the schema object
3233
*/
3334
export function getFieldType(schemaSpec: any, fieldName: string, nestedObject?: boolean) {
3435
if (nestedObject) {
35-
const namePath = fieldName.split('.').join(`.${schemaSpecKey}.`);
36-
return get(schemaSpec, namePath);
36+
const namePath = fieldName.split('.');
37+
const currentField = namePath[0];
38+
const arrayMatch = currentField.match(/(\w+)\[(\d+)\]/);
39+
40+
if (arrayMatch) {
41+
const [, arrayField] = arrayMatch;
42+
const type = schemaSpec[arrayField];
43+
44+
if (type?.[arrayTypeSchemaSpec]) {
45+
// If there are remaining paths and the type is ObjectType (has schemaSpecKey)
46+
if (namePath.length > 1 && type[arrayTypeSchemaSpec][schemaSpecKey]) {
47+
return getFieldType(
48+
type[arrayTypeSchemaSpec][schemaSpecKey],
49+
namePath.slice(1).join('.'),
50+
true
51+
);
52+
}
53+
// Otherwise return the array element type directly
54+
return type[arrayTypeSchemaSpec];
55+
}
56+
return type;
57+
}
58+
59+
const joinedPath = namePath.join(`.${schemaSpecKey}.`);
60+
return get(schemaSpec, joinedPath);
3761
}
3862
return schemaSpec?.[fieldName];
3963
}

src/Schema.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ interface CheckOptions {
1111

1212
export class Schema<DataType = any, ErrorMsgType = string> {
1313
readonly $spec: SchemaDeclaration<DataType, ErrorMsgType>;
14+
1415
private data: PlainObject;
1516
private checkResult: SchemaCheckResult<DataType, ErrorMsgType> = {};
1617

@@ -37,7 +38,7 @@ export class Schema<DataType = any, ErrorMsgType = string> {
3738
return;
3839
}
3940

40-
this.checkResult[fieldName as string] = checkResult;
41+
this.checkResult[fieldName] = checkResult;
4142
}
4243

4344
private setSchemaOptionsForAllType(data: PlainObject) {

src/utils/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
export { default as get } from 'get-value';
2-
export { default as set } from 'set-value';
1+
export { default as get } from 'lodash/get';
2+
export { default as set } from 'lodash/set';
33
export { default as basicEmptyCheck } from './basicEmptyCheck';
44
export { default as checkRequired } from './checkRequired';
55
export { default as createValidator } from './createValidator';

0 commit comments

Comments
 (0)