This commit is contained in:
btea 2025-06-19 09:53:38 +08:00 committed by GitHub
commit f941a0364d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 49 additions and 1 deletions

View File

@ -333,6 +333,41 @@ describe('component props', () => {
}) })
}) })
test('extendValidator custom warn message', async () => {
let warnMsg = ''
vi.spyOn(console, 'warn').mockImplementation(msg => {
warnMsg = msg
})
const Comp = defineComponent({
props: {
foo: {
type: Number,
extendValidator: (name, value, props, warn) => {
if (typeof value !== 'number') {
warn(
'Invalid prop: custom validator check failed for prop "' +
name +
'".',
)
} else if (!Number.isInteger(value)) {
warn(`Invalid prop: ${name}. Expected an integer.`)
}
},
},
bar: {
type: Number,
},
},
template: `<div />`,
})
// Note this one is using the main Vue render so it can compile template
// on the fly
const root = document.createElement('div')
domRender(h(Comp, { foo: 1.1, bar: 2 }), root)
expect(warnMsg).toMatch(`Invalid prop: foo. Expected an integer.`)
})
//#12011 //#12011
test('replace camelize with hyphenate to handle props key', () => { test('replace camelize with hyphenate to handle props key', () => {
const Comp = { const Comp = {

View File

@ -25,6 +25,7 @@ import {
toRawType, toRawType,
} from '@vue/shared' } from '@vue/shared'
import { warn } from './warning' import { warn } from './warning'
import type { WarnFunction } from './warning'
import { import {
type ComponentInternalInstance, type ComponentInternalInstance,
type ComponentOptions, type ComponentOptions,
@ -57,6 +58,12 @@ export interface PropOptions<T = any, D = T> {
required?: boolean required?: boolean
default?: D | DefaultFactory<D> | null | undefined | object default?: D | DefaultFactory<D> | null | undefined | object
validator?(value: unknown, props: Data): boolean validator?(value: unknown, props: Data): boolean
extendValidator?: (
name: string,
value: unknown,
props: Data,
warn: WarnFunction,
) => unknown
/** /**
* @internal * @internal
*/ */
@ -680,7 +687,7 @@ function validateProp(
props: Data, props: Data,
isAbsent: boolean, isAbsent: boolean,
) { ) {
const { type, required, validator, skipCheck } = prop const { type, required, validator, skipCheck, extendValidator } = prop
// required! // required!
if (required && isAbsent) { if (required && isAbsent) {
warn('Missing required prop: "' + name + '"') warn('Missing required prop: "' + name + '"')
@ -707,6 +714,10 @@ function validateProp(
} }
} }
// custom validator // custom validator
if (extendValidator) {
extendValidator(name, value, props, warn)
return
}
if (validator && !validator(value, props)) { if (validator && !validator(value, props)) {
warn('Invalid prop: custom validator check failed for prop "' + name + '".') warn('Invalid prop: custom validator check failed for prop "' + name + '".')
} }

View File

@ -78,6 +78,8 @@ export function warn(msg: string, ...args: any[]): void {
isWarning = false isWarning = false
} }
export type WarnFunction = typeof warn
export function getComponentTrace(): ComponentTraceStack { export function getComponentTrace(): ComponentTraceStack {
let currentVNode: VNode | null = stack[stack.length - 1] let currentVNode: VNode | null = stack[stack.length - 1]
if (!currentVNode) { if (!currentVNode) {