Skip to content

Commit 74d3117

Browse files
author
foxhound87
committed
feat: validation package type inference when extending validation plugins
1 parent 4d6436c commit 74d3117

10 files changed

Lines changed: 297 additions & 367 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 6.12.0 (master)
2+
3+
- Feat: validation package type inference when extending validation plugins
4+
15
# 6.11.2 (master)
26

37
- Fix: `autocomplete` to `autoComplete`

src/Field.ts

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,6 @@ const setupDefaultProp = (
106106
fallback: instance.$initial,
107107
});
108108

109-
interface ValidationAsyncDataInterface {
110-
valid?: boolean;
111-
message?: string;
112-
}
113-
114109
export default class Field extends Base implements FieldInterface {
115110
hasInitialNestedFields = false;
116111
incremental = false;
@@ -171,7 +166,7 @@ export default class Field extends Base implements FieldInterface {
171166

172167
validationErrorStack: string[] = [];
173168
validationFunctionsData: any[] = [];
174-
validationAsyncData: ValidationAsyncDataInterface | undefined;
169+
validationAsyncData: any;
175170
debouncedValidation: any;
176171

177172
disposeValidationOnBlur: any;
@@ -293,7 +288,7 @@ export default class Field extends Base implements FieldInterface {
293288

294289
get checkValidationErrors(): boolean {
295290
return (
296-
(this.validationAsyncData?.valid === false &&
291+
((this.validationAsyncData as any)?.valid === false &&
297292
!_.isEmpty(this.validationAsyncData)) ||
298293
!_.isEmpty(this.validationErrorStack) ||
299294
_.isString(this.errorAsync) ||
@@ -756,7 +751,7 @@ export default class Field extends Base implements FieldInterface {
756751
this.showError = false;
757752
this.errorSync = null;
758753
this.errorAsync = null;
759-
this.validationAsyncData = {};
754+
this.validationAsyncData = undefined;
760755
this.validationFunctionsData = [];
761756
this.validationErrorStack = [];
762757
Promise.resolve().then(action(() => {

src/models/FieldInterface.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface FieldInterface extends BaseInterface {
1616
errorAsync: string | null;
1717
validationErrorStack: string[];
1818
validationFunctionsData: any[];
19+
validationAsyncData: { valid?: boolean, message?: string | null;};
1920
debouncedValidation: any;
2021
autoFocus: boolean;
2122
inputMode: string;
@@ -69,7 +70,7 @@ export interface FieldInterface extends BaseInterface {
6970
// checkValidationPlugins(): void;
7071
initNestedFields(field: any, update: boolean): void;
7172
invalidate(message?: string, deep?: boolean, async?: boolean): void;
72-
setValidationAsyncData(valid: boolean, message: string): void;
73+
setValidationAsyncData(valid?: boolean, message?: string): void;
7374
resetValidation(deep: boolean): void;
7475
clear(deep?: boolean): void;
7576
reset(deep?: boolean): void;

src/models/ValidatorInterface.ts

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
import Form from "../Form";
22
import Field from "../Field";
3-
import {FieldInterface} from "./FieldInterface";
4-
import {FormInterface} from "./FormInterface";
5-
import {StateInterface} from "./StateInterface";
3+
import { FieldInterface } from "./FieldInterface";
4+
import { FormInterface } from "./FormInterface";
5+
import { StateInterface } from "./StateInterface";
66

77
export interface ValidatorConstructor {
88
form: FormInterface;
99
plugins: ValidationPlugins;
1010
}
1111

1212
export interface ValidateOptionsInterface {
13-
showErrors?: boolean,
14-
related?: boolean,
15-
field?: FieldInterface,
16-
path?: string,
13+
showErrors?: boolean;
14+
related?: boolean;
15+
field?: FieldInterface;
16+
path?: string;
1717
}
1818

1919
export type ValidateOptions = string | ValidateOptionsInterface | Form | Field;
@@ -30,47 +30,48 @@ export interface ValidatorInterface {
3030
validateRelatedFields(field: any, showErrors: boolean): void;
3131
}
3232

33-
export type ValidationPlugin = {
34-
class: any,
35-
config?: ValidationPluginConfig,
36-
};
37-
38-
export interface ValidationPlugins {
39-
[key: string]: ValidationPlugin | undefined;
40-
vjf?: ValidationPlugin;
41-
dvr?: ValidationPlugin;
42-
svk?: ValidationPlugin;
43-
yup?: ValidationPlugin;
44-
zod?: ValidationPlugin;
45-
joi?: ValidationPlugin;
46-
}
47-
4833
export type ValidationPackage = any;
4934

50-
export type ExtendPlugin = ({ validator, form }: {
51-
validator: any, // the plugin instance
52-
form: FormInterface
35+
export type ExtendPlugin<TValidator = ValidationPackage> = (args: {
36+
validator: TValidator;
37+
form: FormInterface;
5338
}) => void;
5439

55-
export interface ValidationPluginConfig {
56-
package: ValidationPackage;
40+
export interface ValidationPluginConfig<TValidator = ValidationPackage> {
41+
package: TValidator;
5742
schema?: any;
5843
options?: any;
59-
extend?: ExtendPlugin;
44+
extend?: ExtendPlugin<TValidator>;
6045
}
6146

62-
export interface ValidationPluginConstructor {
63-
config: ValidationPluginConfig;
47+
export interface ValidationPluginConstructor<TValidator = ValidationPackage> {
48+
config: ValidationPluginConfig<TValidator>;
6449
state: StateInterface;
6550
promises: Promise<unknown>[];
6651
}
6752

68-
export interface ValidationPluginInterface extends ValidationPluginConstructor {
69-
validator: ValidatorInterface;
53+
export interface ValidationPluginInterface<TValidator = ValidationPackage>
54+
extends ValidationPluginConstructor<TValidator> {
55+
validator: TValidator;
7056
schema?: any;
71-
extend?: ExtendPlugin;
72-
validate(field: FieldInterface);
73-
class?(constructor: ValidationPluginConstructor): void;
57+
extend?: ExtendPlugin<TValidator>;
58+
validate(field: FieldInterface): void;
59+
class?(constructor: ValidationPluginConstructor<TValidator>): void;
60+
}
61+
62+
export type ValidationPlugin<TValidator = ValidationPackage> = {
63+
class: any;
64+
config?: ValidationPluginConfig<TValidator>;
65+
};
66+
67+
export interface ValidationPlugins {
68+
[key: string]: ValidationPlugin | undefined;
69+
vjf?: ValidationPlugin;
70+
dvr?: ValidationPlugin;
71+
svk?: ValidationPlugin;
72+
yup?: ValidationPlugin;
73+
zod?: ValidationPlugin;
74+
joi?: ValidationPlugin;
7475
}
7576

7677
export type DriversMap = {
@@ -82,5 +83,4 @@ export enum ValidationHooks {
8283
onError = 'onError',
8384
}
8485

85-
8686
export default ValidatorInterface;

src/validators/DVR.ts

Lines changed: 37 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,21 @@
11
import _ from "lodash";
2-
import {
3-
ValidationPlugin,
4-
ValidationPluginConfig,
5-
ValidationPluginConstructor,
6-
ValidationPluginInterface,
7-
} from "../models/ValidatorInterface";
8-
9-
/**
10-
Declarative Validation Rules
11-
12-
const plugins = {
13-
dvr: dvr({
14-
package: validatorjs,
15-
extend: callback,
16-
}),
17-
};
18-
19-
*/
20-
export class DVR implements ValidationPluginInterface {
21-
promises = [];
22-
23-
config = null;
24-
25-
state = null;
26-
27-
extend = null;
28-
29-
validator = null;
2+
import FieldInterface from "src/models/FieldInterface";
3+
import FormInterface from "src/models/FormInterface";
4+
import { ValidationPlugin, ValidationPluginConfig, ValidationPluginConstructor, ValidationPluginInterface } from "src/models/ValidatorInterface";
5+
6+
export class DVR<TValidator = any> implements ValidationPluginInterface<TValidator> {
7+
promises: Promise<any>[];
8+
config: any;
9+
state: any;
10+
extend?: (args: { validator: TValidator; form: FormInterface }) => void;
11+
validator: TValidator;
12+
schema?: any;
3013

3114
constructor({
3215
config,
3316
state = null,
3417
promises = [],
35-
}: ValidationPluginConstructor) {
18+
}: ValidationPluginConstructor<TValidator>) {
3619
this.state = state;
3720
this.promises = promises;
3821
this.extend = config?.extend;
@@ -41,23 +24,21 @@ export class DVR implements ValidationPluginInterface {
4124
}
4225

4326
extendValidator() {
44-
// extend using "extend" callback
45-
if (typeof this.extend === 'function') {
27+
if (typeof this.extend === "function") {
4628
this.extend({
4729
validator: this.validator,
4830
form: this.state.form,
4931
});
5032
}
5133
}
5234

53-
validate(field) {
54-
// get form fields data
35+
validate(field: FieldInterface) {
5536
const data = this.state.form.validatedValues;
5637
this.validateFieldAsync(field, data);
5738
this.validateFieldSync(field, data);
5839
}
5940

60-
makeLabels(validation, field) {
41+
makeLabels(validation: any, field: FieldInterface) {
6142
const labels = { [field.path]: field.label };
6243
_.forIn(validation.rules[field.path], (rule) => {
6344
if (
@@ -85,34 +66,24 @@ export class DVR implements ValidationPluginInterface {
8566
validation.setAttributeNames(labels);
8667
}
8768

88-
validateFieldSync(field, data) {
69+
validateFieldSync(field: FieldInterface, data: any) {
8970
const $rules = this.rules(field.rules, "sync");
90-
// exit if no rules found
9171
if (_.isEmpty($rules[0])) return;
92-
// get field rules
9372
const rules = { [field.path]: $rules };
94-
// create the validator instance
95-
const validation = new this.validator(data, rules);
96-
// set label into errors messages instead key
73+
const validation = new (this.validator as any)(data, rules);
9774
this.makeLabels(validation, field);
98-
// check validation
9975
if (validation.passes()) return;
100-
// the validation is failed, set the field error
10176
field.invalidate(_.head(validation.errors.get(field.path)), false);
10277
}
10378

104-
validateFieldAsync(field, data) {
79+
validateFieldAsync(field: FieldInterface, data: any) {
10580
const $rules = this.rules(field.rules, "async");
106-
// exit if no rules found
10781
if (_.isEmpty($rules[0])) return;
108-
// get field rules
10982
const rules = { [field.path]: $rules };
110-
// create the validator instance
111-
const validation = new this.validator(data, rules);
112-
// set label into errors messages instead key
83+
const validation = new (this.validator as any)(data, rules);
11384
this.makeLabels(validation, field);
11485

115-
const $p = new Promise((resolve) =>
86+
const $p = new Promise((resolve: any) =>
11687
validation.checkAsync(
11788
() => this.handleAsyncPasses(field, resolve),
11889
() => this.handleAsyncFails(field, validation, resolve)
@@ -122,36 +93,40 @@ export class DVR implements ValidationPluginInterface {
12293
this.promises.push($p);
12394
}
12495

125-
handleAsyncPasses(field, resolve) {
96+
handleAsyncPasses(field: FieldInterface, resolve: () => void) {
12697
field.setValidationAsyncData(true);
12798
resolve();
12899
}
129100

130-
handleAsyncFails(field, validation, resolve) {
131-
field.setValidationAsyncData(false, _.head(validation.errors.get(field.path)));
101+
handleAsyncFails(field: FieldInterface, validation: any, resolve: () => void) {
102+
field.setValidationAsyncData(
103+
false,
104+
_.head(validation.errors.get(field.path))
105+
);
132106
this.executeAsyncValidation(field);
133107
resolve();
134108
}
135109

136-
executeAsyncValidation(field) {
110+
executeAsyncValidation(field: FieldInterface) {
137111
if (field.validationAsyncData.valid === false) {
138112
field.invalidate(field.validationAsyncData.message, false, true);
139113
}
140114
}
141115

142-
rules(rules, type) {
116+
rules(rules: any, type: "sync" | "async") {
143117
const $rules = _.isString(rules) ? _.split(rules, "|") : rules;
144-
// eslint-disable-next-line new-cap
145-
const v = new this.validator();
118+
const v = new (this.validator as any)();
146119
return _.filter($rules, ($rule) =>
147120
type === "async"
148-
? v.getRule(_.split($rule, ":")[0]).async
149-
: !v.getRule(_.split($rule, ":")[0]).async
121+
? v.getRule(_.split($rule, ":")[0])?.async
122+
: !v.getRule(_.split($rule, ":")[0])?.async
150123
);
151124
}
152125
}
153126

154-
export default (config?: ValidationPluginConfig): ValidationPlugin => ({
155-
class: DVR,
156-
config,
157-
});
127+
export default <TValidator = any>(
128+
config?: ValidationPluginConfig<TValidator>
129+
): ValidationPlugin<TValidator> => ({
130+
class: DVR<TValidator>,
131+
config,
132+
});

0 commit comments

Comments
 (0)