This commit is contained in:
edison 2025-06-27 09:38:29 +02:00 committed by GitHub
commit 93f67546a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 26 additions and 13 deletions

View File

@ -101,13 +101,14 @@ exports[`sfc reactive props destructure > default values w/ runtime declaration
"import { mergeDefaults as _mergeDefaults } from 'vue' "import { mergeDefaults as _mergeDefaults } from 'vue'
export default { export default {
props: /*@__PURE__*/_mergeDefaults(['foo', 'foo:bar'], { props: /*@__PURE__*/_mergeDefaults(['foo', 'foo:bar', 'baz'], {
foo: 1, foo: 1,
"foo:bar": 'foo-bar' "foo:bar": 'foo-bar',
baz: (props) => (props["foo"])
}), }),
setup(__props) { setup(__props) {
__props.foo
return () => {} return () => {}
} }
@ -142,11 +143,12 @@ export default /*@__PURE__*/_defineComponent({
props: { props: {
foo: { type: Number, required: false, default: 1 }, foo: { type: Number, required: false, default: 1 },
bar: { type: Object, required: false, default: () => ({}) }, bar: { type: Object, required: false, default: () => ({}) },
func: { type: Function, required: false, default: () => {} } func: { type: Function, required: false, default: () => {} },
baz: { type: Object, required: false, default: (props) => (props["bar"]) }
}, },
setup(__props: any) { setup(__props: any) {
__props.bar
return () => {} return () => {}
} }

View File

@ -106,25 +106,28 @@ describe('sfc reactive props destructure', () => {
})`) })`)
assertCode(content) assertCode(content)
}) })
test('default values w/ runtime declaration & key is string', () => { test('default values w/ runtime declaration & key is string', () => {
const { content, bindings } = compile(` const { content, bindings } = compile(`
<script setup> <script setup>
const { foo = 1, 'foo:bar': fooBar = 'foo-bar' } = defineProps(['foo', 'foo:bar']) const { foo = 1, 'foo:bar': fooBar = 'foo-bar', baz = foo } = defineProps(['foo', 'foo:bar', 'baz'])
</script> </script>
`) `)
expect(bindings).toStrictEqual({ expect(bindings).toStrictEqual({
__propsAliases: { __propsAliases: {
fooBar: 'foo:bar', fooBar: 'foo:bar',
}, },
baz: BindingTypes.PROPS,
foo: BindingTypes.PROPS, foo: BindingTypes.PROPS,
'foo:bar': BindingTypes.PROPS, 'foo:bar': BindingTypes.PROPS,
fooBar: BindingTypes.PROPS_ALIASED, fooBar: BindingTypes.PROPS_ALIASED,
}) })
expect(content).toMatch(` expect(content).toMatch(`
props: /*@__PURE__*/_mergeDefaults(['foo', 'foo:bar'], { props: /*@__PURE__*/_mergeDefaults(['foo', 'foo:bar', 'baz'], {
foo: 1, foo: 1,
"foo:bar": 'foo-bar' "foo:bar": 'foo-bar',
baz: (props) => (props["foo"])
}),`) }),`)
assertCode(content) assertCode(content)
}) })
@ -132,7 +135,7 @@ describe('sfc reactive props destructure', () => {
test('default values w/ type declaration', () => { test('default values w/ type declaration', () => {
const { content } = compile(` const { content } = compile(`
<script setup lang="ts"> <script setup lang="ts">
const { foo = 1, bar = {}, func = () => {} } = defineProps<{ foo?: number, bar?: object, func?: () => any }>() const { foo = 1, bar = {}, func = () => {}, baz = bar } = defineProps<{ foo?: number, bar?: object, func?: () => any, baz?: object }>()
</script> </script>
`) `)
// literals can be used as-is, non-literals are always returned from a // literals can be used as-is, non-literals are always returned from a
@ -140,7 +143,8 @@ describe('sfc reactive props destructure', () => {
expect(content).toMatch(`props: { expect(content).toMatch(`props: {
foo: { type: Number, required: false, default: 1 }, foo: { type: Number, required: false, default: 1 },
bar: { type: Object, required: false, default: () => ({}) }, bar: { type: Object, required: false, default: () => ({}) },
func: { type: Function, required: false, default: () => {} } func: { type: Function, required: false, default: () => {} },
baz: { type: Object, required: false, default: (props) => (props["bar"]) }
}`) }`)
assertCode(content) assertCode(content)
}) })

View File

@ -349,13 +349,16 @@ function genDestructuredDefaultValue(
} }
} }
const isDestructuredBinding = ctx.propsDestructuredBindings[value]
// If the default value is a function or is an identifier referencing // If the default value is a function or is an identifier referencing
// external value, skip factory wrap. This is needed when using // external value, skip factory wrap. This is needed when using
// destructure w/ runtime declaration since we cannot safely infer // destructure w/ runtime declaration since we cannot safely infer
// whether the expected runtime prop type is `Function`. // whether the expected runtime prop type is `Function`.
const needSkipFactory = const needSkipFactory =
!inferredType && !inferredType &&
(isFunctionType(unwrapped) || unwrapped.type === 'Identifier') (isFunctionType(unwrapped) ||
(unwrapped.type === 'Identifier' && !isDestructuredBinding))
const needFactoryWrap = const needFactoryWrap =
!needSkipFactory && !needSkipFactory &&
@ -363,13 +366,17 @@ function genDestructuredDefaultValue(
!inferredType?.includes('Function') !inferredType?.includes('Function')
return { return {
valueString: needFactoryWrap ? `() => (${value})` : value, valueString: needFactoryWrap
? isDestructuredBinding
? `(props) => (props["${value}"])`
: `() => (${value})`
: value,
needSkipFactory, needSkipFactory,
} }
} }
} }
// non-comprehensive, best-effort type infernece for a runtime value // non-comprehensive, best-effort type inference for a runtime value
// this is used to catch default value / type declaration mismatches // this is used to catch default value / type declaration mismatches
// when using props destructure. // when using props destructure.
function inferValueType(node: Node): string | undefined { function inferValueType(node: Node): string | undefined {