Skip to content

Commit

Permalink
feat(runtime-core): add skipCheck for prop (#7548)
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz authored Mar 28, 2023
1 parent 1bde9fb commit 63ad77f
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1715,7 +1715,9 @@ export default /*#__PURE__*/_defineComponent({
foo: { type: [Function, null], required: true },
unknown: { type: null, required: true },
unknownUnion: { type: null, required: true },
unknownIntersection: { type: Object, required: true }
unknownIntersection: { type: Object, required: true },
unknownUnionWithBoolean: { type: Boolean, required: true, skipCheck: true },
unknownUnionWithFunction: { type: Function, required: true, skipCheck: true }
},
setup(__props: any, { expose: __expose }) {
__expose();
Expand Down
14 changes: 12 additions & 2 deletions packages/compiler-sfc/__tests__/compileScript.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,8 @@ const emit = defineEmits(['a', 'b'])
unknown: UnknownType
unknownUnion: UnknownType | string
unknownIntersection: UnknownType & Object
unknownUnionWithBoolean: UnknownType | boolean
unknownUnionWithFunction: UnknownType | (() => any)
}>()
</script>`)
assertCode(content)
Expand Down Expand Up @@ -1093,7 +1095,13 @@ const emit = defineEmits(['a', 'b'])
expect(content).toMatch(`unknownUnion: { type: null, required: true }`)
// intersection containing unknown type: narrow to the known types
expect(content).toMatch(
`unknownIntersection: { type: Object, required: true }`
`unknownIntersection: { type: Object, required: true },`
)
expect(content).toMatch(
`unknownUnionWithBoolean: { type: Boolean, required: true, skipCheck: true },`
)
expect(content).toMatch(
`unknownUnionWithFunction: { type: Function, required: true, skipCheck: true }`
)
expect(bindings).toStrictEqual({
string: BindingTypes.PROPS,
Expand Down Expand Up @@ -1131,7 +1139,9 @@ const emit = defineEmits(['a', 'b'])
nonNull: BindingTypes.PROPS,
unknown: BindingTypes.PROPS,
unknownUnion: BindingTypes.PROPS,
unknownIntersection: BindingTypes.PROPS
unknownIntersection: BindingTypes.PROPS,
unknownUnionWithBoolean: BindingTypes.PROPS,
unknownUnionWithFunction: BindingTypes.PROPS
})
})

Expand Down
16 changes: 12 additions & 4 deletions packages/compiler-sfc/src/compileScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -888,11 +888,11 @@ export function compileScript(
}
}
const { type, required } = props[key]
const { type, required, skipCheck } = props[key]
if (!isProd) {
return `${key}: { type: ${toRuntimeTypeString(
type
)}, required: ${required}${
)}, required: ${required}${skipCheck ? ', skipCheck: true' : ''}${
defaultString ? `, ${defaultString}` : ``
} }`
} else if (
Expand Down Expand Up @@ -1964,6 +1964,7 @@ interface PropTypeData {
key: string
type: string[]
required: boolean
skipCheck: boolean
}

function recordType(node: Node, declaredTypes: Record<string, string[]>) {
Expand Down Expand Up @@ -1993,19 +1994,26 @@ function extractRuntimeProps(
m.key.type === 'Identifier'
) {
let type: string[] | undefined
let skipCheck = false
if (m.type === 'TSMethodSignature') {
type = ['Function']
} else if (m.typeAnnotation) {
type = inferRuntimeType(m.typeAnnotation.typeAnnotation, declaredTypes)
// skip check for result containing unknown types
if (type.includes(UNKNOWN_TYPE)) {
type = [`null`]
if (type.includes('Boolean') || type.includes('Function')) {
type = type.filter(t => t !== UNKNOWN_TYPE)
skipCheck = true
} else {
type = ['null']
}
}
}
props[m.key.name] = {
key: m.key.name,
required: !m.optional,
type: type || [`null`]
type: type || [`null`],
skipCheck
}
}
}
Expand Down
9 changes: 7 additions & 2 deletions packages/runtime-core/__tests__/componentProps.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ describe('component props', () => {
arr: { type: Array },
obj: { type: Object },
cls: { type: MyClass },
fn: { type: Function }
fn: { type: Function },
skipCheck: { type: [Boolean, Function], skipCheck: true }
},
setup() {
return () => null
Expand All @@ -349,7 +350,8 @@ describe('component props', () => {
arr: {},
obj: 'false',
cls: {},
fn: true
fn: true,
skipCheck: 'foo'
}),
nodeOps.createElement('div')
)
Expand All @@ -374,6 +376,9 @@ describe('component props', () => {
expect(
`Invalid prop: type check failed for prop "cls". Expected MyClass, got Object`
).toHaveBeenWarned()
expect(
`Invalid prop: type check failed for prop "skipCheck". Expected Boolean | Function, got String with value "foo".`
).not.toHaveBeenWarned()
})

// #3495
Expand Down
5 changes: 3 additions & 2 deletions packages/runtime-core/src/componentProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface PropOptions<T = any, D = T> {
required?: boolean
default?: D | DefaultFactory<D> | null | undefined | object
validator?(value: unknown): boolean
skipCheck?: boolean
}

export type PropType<T> = PropConstructor<T> | PropConstructor<T>[]
Expand Down Expand Up @@ -608,7 +609,7 @@ function validateProp(
prop: PropOptions,
isAbsent: boolean
) {
const { type, required, validator } = prop
const { type, required, validator, skipCheck } = prop
// required!
if (required && isAbsent) {
warn('Missing required prop: "' + name + '"')
Expand All @@ -619,7 +620,7 @@ function validateProp(
return
}
// type check
if (type != null && type !== true) {
if (type != null && type !== true && !skipCheck) {
let isValid = false
const types = isArray(type) ? type : [type]
const expectedTypes = []
Expand Down

0 comments on commit 63ad77f

Please sign in to comment.