Repository: sveltejs/eslint-plugin-svelte
Pull request #1146 (2025-03-19 11:36:00)

fix(no-unused-props): resolve false positives on props with default values or $bindable usage
close: https://github.com/sveltejs/eslint-plugin-svelte/issues/1145

changeset-bot[bot]: ###  🦋  Changeset detected

Latest commit: 8676dca4c03dd818e95a3fb36e01a6e6a0bd7e2b

**The changes in this PR will be included in the next version bump.**

<details><summary>This PR includes changesets to release 1 package</summary>

  | Name                 | Type  |
| -------------------- | ----- |
| eslint-plugin-svelte | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/sveltejs/eslint-plugin-svelte/new/fix/1145?filename=.changeset/four-toys-listen.md&value=---%0A%22eslint-plugin-svelte%22%3A%20patch%0A---%0A%0Afix(no-unused-props)%3A%20resolve%20false%20positives%20on%20props%20with%20default%20values%20or%20%24bindable%20usage%0A)


github-actions[bot]: <!-- posted by pkg.pr.new-comment.mjs -->

## Try the Instant Preview in Online Playground

[ESLint Online Playground](https://eslint-online-playground.netlify.app/?overrideDeps=%7B%22eslint-plugin-svelte%22%3A%22https%3A%2F%2Fpkg.pr.new%2Feslint-plugin-svelte%401146%22%7D#eslint-plugin-svelte%20with%20typescript)

## Install the Instant Preview to Your Local

```
npm i https://pkg.pr.new/eslint-plugin-svelte@1146
```

## Published Instant Preview Packages:

- eslint-plugin-svelte: https://pkg.pr.new/eslint-plugin-svelte@1146

[View Commit](https://github.com/sveltejs/eslint-plugin-svelte/commit/8676dca4c03dd818e95a3fb36e01a6e6a0bd7e2b)

Patch:
diff --git a/.changeset/few-singers-marry.md b/.changeset/few-singers-marry.md
new file mode 100644
index 0000000..4e924df
--- /dev/null
+++ b/.changeset/few-singers-marry.md
@@ -0,0 +1,5 @@
+---
+'eslint-plugin-svelte': patch
+---
+
+fix(no-unused-props): resolve false positives on props with default values or $bindable usage
diff --git a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts
index 5de3133..2ea58fd 100644
--- a/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts
+++ b/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts
@@ -369,10 +369,17 @@ export default createRule('no-unused-props', {
 				if (node.id.type === 'ObjectPattern') {
 					usedProps = getUsedPropertiesFromPattern(node.id);
 					if (usedProps.size === 0) return;
-					const identifiers = node.id.properties
-						.filter((p): p is TSESTree.Property => p.type === 'Property')
-						.map((p) => p.value)
-						.filter((v): v is TSESTree.Identifier => v.type === 'Identifier');
+					const identifiers: TSESTree.Identifier[] = [];
+					for (const p of node.id.properties) {
+						if (p.type !== 'Property') {
+							continue;
+						}
+						if (p.value.type === 'Identifier') {
+							identifiers.push(p.value);
+						} else if (p.value.type === 'AssignmentPattern' && p.value.left.type === 'Identifier') {
+							identifiers.push(p.value.left);
+						}
+					}
 					for (const identifier of identifiers) {
 						const paths = getUsedNestedPropertyNames(identifier);
 						usedPaths.push(...paths.map((path) => [identifier.name, ...path]));


Test patch:
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/bindable-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/bindable-input.svelte
new file mode 100644
index 0000000..ef2c5e9
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/bindable-input.svelte
@@ -0,0 +1,9 @@
+<script lang="ts">
+	interface Props {
+		selected: { value: string };
+	}
+
+	let { selected = $bindable() }: Props = $props();
+</script>
+
+<input type="text" bind:value={selected.value} />
diff --git a/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/default-value-input.svelte b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/default-value-input.svelte
new file mode 100644
index 0000000..48d6887
--- /dev/null
+++ b/packages/eslint-plugin-svelte/tests/fixtures/rules/no-unused-props/valid/default-value-input.svelte
@@ -0,0 +1,14 @@
+<script lang="ts">
+	interface Props {
+		newTaskAttributes: { attribute: number; attribute2: string };
+	}
+
+	const { newTaskAttributes = { attribute: 0, attribute2: '' } }: Props = $props();
+</script>
+
+<div class="col-span-full">
+	<div>
+		{newTaskAttributes.attribute}
+		{newTaskAttributes.attribute2}
+	</div>
+</div>