Vue version
3.5.39
Link to minimal reproduction
https://play.vuejs.org/#eNrdUk1zmzAQ/StbLtgzNhzaE8XuGDeH5hBnmhy5UFhspULSSIvtDOa/V4L4g2niGV9z0+57evv2o/EWSgXbGr3IiwkrxTPCeSoA4uWG8WIBZiN3qy3qkssdhD30ZTqFZWYQFjCdXrCT6+ykZ8fhRSEbmlwzRWCQagU8E+tZ6pFJPYuySklN8Gal1LICPwj70Ln2vw85yZCTHDlx2BeZexMrnUtRsnXwYqSwbTfOZOrlslKMo14pYlLY8hF0iMMybtu573Kka5wc8/kG87/v5F/M3uVS71GjQb3F1DthlOk1Ug/fPT3g3r5PYCWLmlv2FfA3Gslr57GnJbUorO0LXuf2VzcVJtbP5m5PKMyxKWfUMduOn3p2QssrrZ/tfg2+df9S0dopnnfw/+UUbDsfHMIMmgbun1YPgSFtPbHydXRJGEPbwoheFdrKDbiHLIe31LbjOHTCN92PU4KjxrMLZuCTlJyY8uEAPnLOlGGmC4gRR99phuHbfUfwx7IxE2AL5lhAybShVNgTMgRKS2UWVnPHaPMTy6zmZEYFlkzgo8PiZtDFj7PcYWirnY/GExiyI7Cr7bQKaMfDIz6f92cefxINf3+4g+SmHQw0D6eV3LaD9h/V882S
Steps to reproduce
Steps to Reproduce
- Define a Vue 3 SFC with
<script setup lang="ts">.
- Declare a prop using
defineProps with a union type OverflowType | boolean, where OverflowType is a string literal union 'tooltip' | 'ellipsis' | 'title'.
- Set a default value of
undefined using withDefaults for that prop.
- In a parent component, use the child component with the prop as an attribute without a value:
<MyComponent showOverflow />.
- Observe the actual value of the prop.
<!-- Child.vue -->
<template>
<div>showOverflow = {{ JSON.stringify(showOverflow) }} (type: {{ typeof showOverflow }})</div>
</template>
<script setup lang="ts">
type OverflowType = 'tooltip' | 'ellipsis' | 'title'
// Case A: boolean placed first
const propsA = withDefaults(defineProps<{ showOverflow?: boolean | OverflowType }>(), { showOverflow: undefined })
// Case B: OverflowType placed first
const propsB = withDefaults(defineProps<{ showOverflow?: OverflowType | boolean }>(), { showOverflow: undefined })
</script>
<!-- Parent.vue -->
<template>
<ChildA showOverflow />
<!-- Case A -->
<ChildB showOverflow />
<!-- Case B -->
</template>
What is expected?
Expected Result
Regardless of the order of types in the union, the behavior for the same prop when passed as an attribute without a value should be consistent. Users would expect either:
- Always pass
true (aligning with the HTML boolean attribute semantics – <input disabled> means true); or
- Always pass
"" (empty string), as an attribute without a value in HTML is essentially an empty string attribute.
But the behavior should never depend on the order of type declarations.
More importantly, the user has explicitly set a default value of undefined via withDefaults. The expected behavior is:
- When the parent does not pass the prop at all → use
undefined.
- When the parent passes the prop as an attribute without a value → it should be treated as an explicit "presence" signal, and thus the value should be
true (consistent with boolean attribute semantics).
What is actually happening?
Actual Result
- Case A (
boolean | OverflowType): showOverflow is true (boolean)
- Case B (
OverflowType | boolean): showOverflow is "" (string)
Both results ignore the default value undefined set via withDefaults, because the prop is considered "passed" (even without a value), so the default is not applied.
System Info
System:
OS: Windows 11 10.0.22631
CPU: (28) x64 Intel(R) Core(TM) i7-14700
Memory: 9.05 GB / 31.68 GB
Binaries:
Binaries:
Node: 20.20.2 - C:\install\nodejs\v20.20.2\node.EXE
npm: 10.8.2 - C:\install\nodejs\v20.20.2\npm.CMD
Browsers:
Chrome: 128.0.6613.85
Edge: Chromium (149.0.4022.80)
npmPackages:
vue: ^3.2.41 => 3.5.39
Any additional comments?
No response
Vue version
3.5.39
Link to minimal reproduction
https://play.vuejs.org/#eNrdUk1zmzAQ/StbLtgzNhzaE8XuGDeH5hBnmhy5UFhspULSSIvtDOa/V4L4g2niGV9z0+57evv2o/EWSgXbGr3IiwkrxTPCeSoA4uWG8WIBZiN3qy3qkssdhD30ZTqFZWYQFjCdXrCT6+ykZ8fhRSEbmlwzRWCQagU8E+tZ6pFJPYuySklN8Gal1LICPwj70Ln2vw85yZCTHDlx2BeZexMrnUtRsnXwYqSwbTfOZOrlslKMo14pYlLY8hF0iMMybtu573Kka5wc8/kG87/v5F/M3uVS71GjQb3F1DthlOk1Ug/fPT3g3r5PYCWLmlv2FfA3Gslr57GnJbUorO0LXuf2VzcVJtbP5m5PKMyxKWfUMduOn3p2QssrrZ/tfg2+df9S0dopnnfw/+UUbDsfHMIMmgbun1YPgSFtPbHydXRJGEPbwoheFdrKDbiHLIe31LbjOHTCN92PU4KjxrMLZuCTlJyY8uEAPnLOlGGmC4gRR99phuHbfUfwx7IxE2AL5lhAybShVNgTMgRKS2UWVnPHaPMTy6zmZEYFlkzgo8PiZtDFj7PcYWirnY/GExiyI7Cr7bQKaMfDIz6f92cefxINf3+4g+SmHQw0D6eV3LaD9h/V882S
Steps to reproduce
Steps to Reproduce
<script setup lang="ts">.definePropswith a union typeOverflowType | boolean, whereOverflowTypeis a string literal union'tooltip' | 'ellipsis' | 'title'.undefinedusingwithDefaultsfor that prop.<MyComponent showOverflow />.What is expected?
Expected Result
Regardless of the order of types in the union, the behavior for the same prop when passed as an attribute without a value should be consistent. Users would expect either:
true(aligning with the HTML boolean attribute semantics –<input disabled>meanstrue); or""(empty string), as an attribute without a value in HTML is essentially an empty string attribute.But the behavior should never depend on the order of type declarations.
More importantly, the user has explicitly set a default value of
undefinedviawithDefaults. The expected behavior is:undefined.true(consistent with boolean attribute semantics).What is actually happening?
Actual Result
boolean | OverflowType):showOverflowistrue(boolean)OverflowType | boolean):showOverflowis""(string)Both results ignore the default value
undefinedset viawithDefaults, because the prop is considered "passed" (even without a value), so the default is not applied.System Info
System: OS: Windows 11 10.0.22631 CPU: (28) x64 Intel(R) Core(TM) i7-14700 Memory: 9.05 GB / 31.68 GB Binaries: Binaries: Node: 20.20.2 - C:\install\nodejs\v20.20.2\node.EXE npm: 10.8.2 - C:\install\nodejs\v20.20.2\npm.CMD Browsers: Chrome: 128.0.6613.85 Edge: Chromium (149.0.4022.80) npmPackages: vue: ^3.2.41 => 3.5.39Any additional comments?
No response