feat(no-mutating-props): report nested mutation of defineModel() refs - #3131
Draft
faizanu94 wants to merge 1 commit into
Draft
feat(no-mutating-props): report nested mutation of defineModel() refs#3131faizanu94 wants to merge 1 commit into
defineModel() refs#3131faizanu94 wants to merge 1 commit into
Conversation
🦋 Changeset detectedLatest commit: 0f53763 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
`defineModel()` returns a ref that bridges a prop and its `update:modelValue`
emit. Reassigning `model.value` triggers the emit, but mutating a nested
property in place (`model.value.foo = 1`, `model.value.items.push(1)`,
`Object.assign(model.value, {...})`) bypasses the setter, never emits, and
silently desyncs the parent.
`vue/no-mutating-props` already reports this class of bug for `defineProps()`
but ignored `defineModel()`, since it only registered `onDefinePropsEnter`.
This wires `defineModel()` bindings into the same `findMutating` pipeline via
`onDefineModelEnter`, reusing the existing detection logic. Reassigning the ref
itself remains allowed, and `shallowOnly` continues to permit nested mutation.
Refs vuejs#3130
faizanu94
force-pushed
the
feat/no-mutating-props-definemodel
branch
from
August 25, 2026 04:11
0790b22 to
0f53763
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #3130
Summary
defineModel()returns a ref that bridges a prop and itsupdate:modelValueemit. Reassigningmodel.valuegoes through the ref setter and emits the update, but mutating a nested property in place bypasses the setter, never emits, and silently desyncs the parent:vue/no-mutating-propsalready reports this exact class of bug fordefineProps(), but ignoreddefineModel()because it only registeredonDefinePropsEnter. This wiresdefineModel()bindings into the sameutils.findMutatingpipeline used for destructured props, via the existingonDefineModelEnterhook (added in #2360). This follows the same "teach an existing rule aboutdefineModel()" pattern as #2360 and #3032.Behavior
defineModel()ref: property assignment, index assignment, arbitrary depth,++/delete, mutating array methods (push,splice, …), andObject.assign(model.value, …).model.value = next), which is the correct update pattern.shallowOnly: truecontinues to permit nested mutation, consistent with its meaning for props.defineProps()/ Options API behavior.Known limitation (deliberate, out of scope for this PR)
This is a syntactic check on direct
model.value.<path>mutation. It does not track mutation through an intermediate alias (const i = model.value.items[0]; i.x = 1), which would require data-flow analysis. Happy to note this in the docs or track it separately if preferred.Test plan
tests/lib/rules/no-mutating-props.test.ts(nested property/index/depth, mutating calls,Object.assign, named model, correct-reassign patterns, unrelated locals,shallowOnly, and adefinePropsregression guard).npx vitest run tests/lib→ 288 files, 11628 tests pass.npx eslinton changed files andnpx markdownlint "**/*.md"clean.npm run buildsucceeds.minorchangeset.