Vue merges the properties declared in a component’s props, data, computed, methods, and
setup blocks onto a single instance namespace, so the component and its template can access any of them the same way (for example
this.name or {{ name }}). If the same name is declared in more than one of these blocks, only one of them survives, and
which one wins depends on Vue’s internal merge order rather than anything visible in the source. In development, Vue detects most of these collisions
and logs a console warning naming the conflicting option groups, but that check is stripped out of production builds, so the overwrite still happens
silently there.
This rule also checks <script setup> components: it flags a prop declared with defineProps() (including through
withDefaults()) that shares its name with another top-level binding declared in the same <script setup> block. Vue
does not emit a runtime warning for this case; the local binding silently takes precedence over the prop when the template is compiled.
To fix this, rename one of the colliding declarations so that each name appears in only one place.
<script>
export default {
props: {
name: String
},
data() {
return {
name: null // Noncompliant: "name" is already declared as a prop
};
},
methods: {
name() {} // Noncompliant: "name" is already declared as a prop
}
}
</script>
<script>
export default {
props: {
name: String
},
data() {
return {
fullName: null
};
},
methods: {
formatName() {}
}
}
</script>
<script setup>
const props = defineProps(['name']);
const name = ref(''); // Noncompliant: "name" collides with the "name" prop
</script>
<script setup>
const props = defineProps(['name']);
const displayName = ref('');
</script>