fix(form): fix async-validator related types

This commit is contained in:
07akioni 2021-05-19 02:23:47 +08:00
parent 7e9784a6d3
commit df73044c24
5 changed files with 71 additions and 50 deletions

View File

@ -11,18 +11,18 @@ You can use `n-form-item` alone, without `n-form`.
```js
import { defineComponent, ref } from 'vue'
const lyrics = 'It is not in Form'
const message = 'It is not in Form'
export default defineComponent({
setup () {
const valueRef = ref(lyrics)
const valueRef = ref(message)
return {
value: valueRef,
rule: {
trigger: ['input', 'blur'],
validator () {
if (valueRef.value !== lyrics) {
return new Error(lyrics)
if (valueRef.value !== message) {
return new Error(message)
}
}
}

View File

@ -11,18 +11,18 @@
```js
import { defineComponent, ref } from 'vue'
const lyrics = '它不在 Form 里面'
const message = '它不在 Form 里面'
export default defineComponent({
setup () {
const valueRef = ref(lyrics)
const valueRef = ref(message)
return {
value: valueRef,
rule: {
trigger: ['input', 'blur'],
validator () {
if (valueRef.value !== lyrics) {
return new Error(lyrics)
if (valueRef.value !== message) {
return new Error(message)
}
}
}

View File

@ -13,7 +13,7 @@ import {
Transition,
renderSlot
} from 'vue'
import Schema, { ErrorList, ValidateOption } from 'async-validator'
import Schema, { ErrorList, RuleItem, ValidateOption } from 'async-validator'
import { get } from 'lodash-es'
import { createId } from 'seemly'
import { formItemInjectionKey } from '../../_mixins/use-form-item'
@ -36,6 +36,7 @@ import {
LabelPlacement,
ValidateCallback,
ValidationTrigger,
FormItemRuleValidatorParams,
FormItemRuleValidator,
FormItemValidateOptions,
FormItemInst,
@ -87,27 +88,38 @@ export type FormItemProps = ExtractPublicPropTypes<typeof formItemProps>
export const formItemPropKeys = keysOf(formItemProps)
// Wrapped Validator is to be passed into async-validator
// In their source code, validator can be a asyncValidator.
// asyncValidator will non-promise return value will be ignored.
// We need to deal with some type quirks.
type WrappedValidator = (
...args: Parameters<FormItemRuleValidator>
) => boolean | Error | Promise<boolean | Error> | undefined
function wrapValidator (validator: FormItemRuleValidator): WrappedValidator {
...args: FormItemRuleValidatorParams
) => boolean | Error | Error[] | Promise<void> | undefined
// wrap sync validator
function wrapValidator (
validator: FormItemRuleValidator,
async: boolean
): WrappedValidator {
return (...args: Parameters<FormItemRuleValidator>) => {
try {
const validateResult = validator(...args)
if (
typeof validateResult === 'boolean' ||
validateResult instanceof Error ||
validateResult?.then
(!async &&
(typeof validateResult === 'boolean' ||
validateResult instanceof Error ||
Array.isArray(validateResult))) || // Error[]
(validateResult as any)?.then
) {
return validateResult
return validateResult as any
} else if (validateResult === undefined) {
return true
} else {
warn(
'form-item/validate',
`You return a ${typeof validateResult} ` +
'typed value in the validator method, which is not recommended. Please ' +
'use `boolean`, `Error` or `Promise` typed value instead.'
'typed value in the validator method, which is not recommended. Please use ' +
(async ? '`Promise`' : '`boolean`, `Error` or `Promise`') +
' typed value instead.'
)
return true
}
@ -119,6 +131,8 @@ function wrapValidator (validator: FormItemRuleValidator): WrappedValidator {
"`n-form` or `n-form-item` won't be called in this validation."
)
console.error(err)
// If returns undefined, async-validator won't trigger callback
// so the result will be abandoned, which means not true and not false
return undefined
}
}
@ -138,10 +152,8 @@ export default defineComponent({
const formItemSizeRefs = formItemSize(props)
const formItemMiscRefs = formItemMisc(props)
const { validationErrored: validationErroredRef } = formItemMiscRefs
const {
mergedRequired: mergedRequiredRef,
mergedRules: mergedRulesRef
} = formItemRule(props)
const { mergedRequired: mergedRequiredRef, mergedRules: mergedRulesRef } =
formItemRule(props)
const { mergedSize: mergedSizeRef } = formItemSizeRefs
const { mergedLabelPlacement: labelPlacementRef } = formItemMiscRefs
const explainsRef = ref<string[]>([])
@ -247,28 +259,31 @@ export default defineComponent({
const { value: rules } = mergedRulesRef
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const value = NForm ? get(NForm.model, path!, null) : undefined
const activeRules = (!trigger
? rules
: rules.filter((rule) => {
// if (rule.trigger === undefined) return true
if (Array.isArray(rule.trigger)) {
return rule.trigger.includes(trigger)
} else {
return rule.trigger === trigger
}
})
const activeRules = (
!trigger
? rules
: rules.filter((rule) => {
// if (rule.trigger === undefined) return true
if (Array.isArray(rule.trigger)) {
return rule.trigger.includes(trigger)
} else {
return rule.trigger === trigger
}
})
)
.filter(shouldRuleBeApplied)
.map((rule) => {
const shallowClonedRule = Object.assign({}, rule)
if (shallowClonedRule.validator) {
shallowClonedRule.validator = wrapValidator(
shallowClonedRule.validator
shallowClonedRule.validator,
false
)
}
if (shallowClonedRule.asyncValidator) {
shallowClonedRule.asyncValidator = wrapValidator(
shallowClonedRule.asyncValidator
shallowClonedRule.asyncValidator,
true
) as any
}
return shallowClonedRule
@ -279,8 +294,8 @@ export default defineComponent({
})
}
const mergedPath = path ?? '__n_no_path__'
const validator = new Schema({ [mergedPath]: activeRules })
return new Promise((resolve, reject) => {
const validator = new Schema({ [mergedPath]: activeRules as RuleItem[] })
return new Promise((resolve) => {
void validator.validate(
{ [mergedPath]: value },
options,
@ -345,10 +360,8 @@ export default defineComponent({
[createKey('feedbackHeight', size)]: feedbackHeight,
[createKey('labelPadding', direction)]: labelPadding,
[createKey('labelTextAlign', direction)]: labelTextAlign,
[createKey(
createKey('labelFontSize', labelPlacement),
size
)]: labelFontSize
[createKey(createKey('labelFontSize', labelPlacement), size)]:
labelFontSize
}
} = themeRef.value
return {

View File

@ -1,4 +1,4 @@
import { InjectionKey } from '@vue/runtime-core'
import { InjectionKey } from 'vue'
import { ErrorList, RuleItem, ValidateOption } from 'async-validator'
import { FormSetupProps } from './Form'
@ -6,14 +6,23 @@ export interface FormRules {
[path: string]: FormRules | FormItemRule | FormItemRule[]
}
export type FormItemRuleValidator = (
...args: Parameters<RuleItem['validator'] & {}>
) => boolean | Error | Promise<boolean> | Promise<Error> | any
export type FormItemRuleValidatorParams = Parameters<
NonNullable<RuleItem['validator']>
>
export type FormItemRule = RuleItem & {
export type FormItemRuleValidator = (
...args: FormItemRuleValidatorParams
) => boolean | Error | Error[] | Promise<void> | undefined
// In src of async-validator, any non-promise of asyncValidator will be abadoned
export type FormItemRuleAsyncValidator = (
...args: FormItemRuleValidatorParams
) => Promise<void> | undefined
export type FormItemRule = Omit<RuleItem, 'validator' | 'asyncValidator'> & {
trigger?: ValidationTrigger | string | Array<ValidationTrigger | string>
validator?: FormItemRuleValidator
asyncValidator?: FormItemRuleValidator
asyncValidator?: FormItemRuleAsyncValidator
}
export interface FormItemValidateOptions {
@ -50,9 +59,8 @@ export type FormItemRowRef = FormItemInst
export type FormInjection = FormSetupProps
export const formInjectionKey: InjectionKey<FormInjection> = Symbol('form')
export const formItemInstsInjectionKey: InjectionKey<unknown> = Symbol(
'formItemInsts'
)
export const formItemInstsInjectionKey: InjectionKey<unknown> =
Symbol('formItemInsts')
export type LabelAlign = 'left' | 'center' | 'right'
export type LabelPlacement = 'left' | 'top'

View File

@ -1 +1 @@
export default '2.7.4'
export default '2.8.0'