Skip to content

Commit 268a372

Browse files
authored
feat: add no-unused-props rule (#1061)
1 parent bb771cf commit 268a372

File tree

84 files changed

+1240
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+1240
-0
lines changed

.changeset/twelve-beers-talk.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-svelte': minor
3+
---
4+
5+
feat: add `no-unused-props` rule

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ These rules relate to better ways of doing things to help you avoid problems:
303303
| [svelte/no-reactive-literals](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-reactive-literals/) | don't assign literal values in reactive statements | :star::bulb: |
304304
| [svelte/no-svelte-internal](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-svelte-internal/) | svelte/internal will be removed in Svelte 6. | :star: |
305305
| [svelte/no-unused-class-name](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-class-name/) | disallow the use of a class in the template without a corresponding style | |
306+
| [svelte/no-unused-props](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-props/) | Warns about defined Props properties that are unused | :star: |
306307
| [svelte/no-unused-svelte-ignore](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: |
307308
| [svelte/no-useless-children-snippet](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-children-snippet/) | disallow explicit children snippet where it's not needed | :star: |
308309
| [svelte/no-useless-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :star::wrench: |
File renamed without changes.

docs/rules.md

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ These rules relate to better ways of doing things to help you avoid problems:
6060
| [svelte/no-reactive-literals](./rules/no-reactive-literals.md) | don't assign literal values in reactive statements | :star::bulb: |
6161
| [svelte/no-svelte-internal](./rules/no-svelte-internal.md) | svelte/internal will be removed in Svelte 6. | :star: |
6262
| [svelte/no-unused-class-name](./rules/no-unused-class-name.md) | disallow the use of a class in the template without a corresponding style | |
63+
| [svelte/no-unused-props](./rules/no-unused-props.md) | Warns about defined Props properties that are unused | :star: |
6364
| [svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md) | disallow unused svelte-ignore comments | :star: |
6465
| [svelte/no-useless-children-snippet](./rules/no-useless-children-snippet.md) | disallow explicit children snippet where it's not needed | :star: |
6566
| [svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :star::wrench: |

docs/rules/no-unused-props.md

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
---
2+
pageClass: 'rule-details'
3+
sidebarDepth: 0
4+
title: 'svelte/no-unused-props'
5+
description: 'Warns about defined Props properties that are unused'
6+
---
7+
8+
# svelte/no-unused-props
9+
10+
> Warns about defined Props properties that are unused
11+
12+
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
13+
- :gear: This rule is included in `"plugin:svelte/recommended"`.
14+
15+
## :book: Rule Details
16+
17+
This rule reports properties that are defined in Props but never used in the component code.
18+
It helps to detect dead code and improve component clarity by ensuring that every declared prop is utilized.
19+
20+
This rule checks various usage patterns of props:
21+
22+
- Direct property access
23+
- Destructuring assignment
24+
- Method calls
25+
- Computed property access
26+
- Object spread
27+
- Constructor calls (new expressions)
28+
- Assignment to other variables
29+
- Index signatures (e.g. `[key: string]: unknown`)
30+
31+
Additionally, this rule checks if index signatures are properly used. When an index signature is defined but not captured using the rest operator (`...`), the rule will suggest using it.
32+
33+
Note: Properties of class types are not checked for usage, as they might be used in other parts of the application.
34+
35+
:warning: This rule requires `@typescript-eslint/parser` to work. Make sure you have installed `@typescript-eslint/parser` and configured it in your ESLint configuration. Therefore, the rule violations cannot be seen in the examples on this page because this documentation does not use `@typescript-eslint/parser`.
36+
37+
<!--eslint-skip-->
38+
39+
```svelte
40+
<!-- ✓ Good Examples -->
41+
<script lang="ts">
42+
/* eslint svelte/no-unused-props: "error" */
43+
// Direct property access
44+
const props: { value: string } = $props();
45+
console.log(props.value);
46+
</script>
47+
```
48+
49+
```svelte
50+
<!-- ✓ Good Examples -->
51+
<script lang="ts">
52+
/* eslint svelte/no-unused-props: "error" */
53+
// Destructuring assignment
54+
const { width, height }: { width: number; height: number } = $props();
55+
console.log(width, height);
56+
</script>
57+
```
58+
59+
```svelte
60+
<!-- ✓ Good Examples -->
61+
<script lang="ts">
62+
/* eslint svelte/no-unused-props: "error" */
63+
// Class properties are not checked
64+
class User {
65+
constructor(
66+
public name: string,
67+
public age: number
68+
) {}
69+
}
70+
type Props = {
71+
user: User;
72+
};
73+
const props: Props = $props();
74+
console.log(props.user.name); // age is not reported as unused
75+
</script>
76+
```
77+
78+
```svelte
79+
<!-- ✓ Good Examples -->
80+
<script lang="ts">
81+
/* eslint svelte/no-unused-props: "error" */
82+
// Method calls
83+
const props2: { callback: () => void } = $props();
84+
props2.callback();
85+
</script>
86+
```
87+
88+
```svelte
89+
<!-- ✓ Good Examples -->
90+
<script lang="ts">
91+
/* eslint svelte/no-unused-props: "error" */
92+
// Computed property access
93+
const props3: { 'data-value': string } = $props();
94+
const value = props3['data-value'];
95+
</script>
96+
```
97+
98+
```svelte
99+
<!-- ✓ Good Examples -->
100+
<script lang="ts">
101+
/* eslint svelte/no-unused-props: "error" */
102+
// Constructor calls
103+
const props4: { config: { new (): any } } = $props();
104+
new props4.config();
105+
</script>
106+
```
107+
108+
```svelte
109+
<!-- ✓ Good Examples -->
110+
<script lang="ts">
111+
/* eslint svelte/no-unused-props: "error" */
112+
// Using index signature with rest operator
113+
interface Props {
114+
a: number;
115+
[key: string]: unknown;
116+
}
117+
let { a, ...rest }: Props = $props();
118+
console.log(rest);
119+
</script>
120+
```
121+
122+
```svelte
123+
<!-- ✗ Bad Examples -->
124+
<script lang="ts">
125+
/* eslint svelte/no-unused-props: "error" */
126+
// Unused property 'b'
127+
const props: { a: string; b: number } = $props();
128+
console.log(props.a);
129+
</script>
130+
```
131+
132+
```svelte
133+
<!-- ✗ Bad Examples -->
134+
<script lang="ts">
135+
/* eslint svelte/no-unused-props: "error" */
136+
// Unused property in destructuring
137+
const { x }: { x: number; y: number } = $props();
138+
console.log(x);
139+
</script>
140+
```
141+
142+
```svelte
143+
<!-- ✗ Bad Examples -->
144+
<script lang="ts">
145+
/* eslint svelte/no-unused-props: "error" */
146+
// Unused index signature
147+
interface Props {
148+
a: number;
149+
[key: string]: unknown; // This will be reported
150+
}
151+
let { a }: Props = $props();
152+
</script>
153+
```
154+
155+
## :wrench: Options
156+
157+
```js
158+
{
159+
"svelte/no-unused-props": ["error", {
160+
// Whether to check properties from imported types
161+
"checkImportedTypes": false,
162+
// Patterns to ignore when checking for unused props
163+
"ignorePatterns": []
164+
}]
165+
}
166+
```
167+
168+
- `checkImportedTypes` ... Controls whether to check properties from imported types. Default is `false`.
169+
- `ignorePatterns` ... Patterns to ignore when checking for unused props. Default is an empty array.
170+
171+
Examples:
172+
173+
```svelte
174+
<!-- ✓ Good Examples -->
175+
<script lang="ts">
176+
/* eslint svelte/no-unused-props: ["error", { "checkImportedTypes": true }] */
177+
// Check properties from imported types
178+
import type { BaseProps } from './types';
179+
interface Props extends BaseProps {
180+
age: number;
181+
}
182+
let { name, age }: Props = $props();
183+
console.log(name, age);
184+
</script>
185+
```
186+
187+
```svelte
188+
<!-- ✓ Good Examples -->
189+
<script lang="ts">
190+
/* eslint svelte/no-unused-props: ["error", { "ignorePatterns": ["^_"] }] */
191+
// Ignore properties starting with underscore
192+
interface Props {
193+
_internal: string;
194+
value: number;
195+
}
196+
let { value }: Props = $props();
197+
console.log(value);
198+
</script>
199+
```
200+
201+
## :gear: Required Configuration
202+
203+
This rule requires `@typescript-eslint/parser` to work. Please refer to the [User Guide](../user-guide.md) for more information.
204+
205+
## :mag: Implementation
206+
207+
- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts)
208+
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-unused-props.ts)

packages/eslint-plugin-svelte/src/configs/flat/recommended.ts

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const config: Linter.Config[] = [
3232
'svelte/no-store-async': 'error',
3333
'svelte/no-svelte-internal': 'error',
3434
'svelte/no-unknown-style-directive-property': 'error',
35+
'svelte/no-unused-props': 'error',
3536
'svelte/no-unused-svelte-ignore': 'error',
3637
'svelte/no-useless-children-snippet': 'error',
3738
'svelte/no-useless-mustaches': 'error',

packages/eslint-plugin-svelte/src/rule-types.ts

+10
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ export interface RuleOptions {
261261
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-class-name/
262262
*/
263263
'svelte/no-unused-class-name'?: Linter.RuleEntry<SvelteNoUnusedClassName>
264+
/**
265+
* Warns about defined Props properties that are unused
266+
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-props/
267+
*/
268+
'svelte/no-unused-props'?: Linter.RuleEntry<SvelteNoUnusedProps>
264269
/**
265270
* disallow unused svelte-ignore comments
266271
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/
@@ -517,6 +522,11 @@ type SvelteNoUnknownStyleDirectiveProperty = []|[{
517522
type SvelteNoUnusedClassName = []|[{
518523
allowedClassNames?: string[]
519524
}]
525+
// ----- svelte/no-unused-props -----
526+
type SvelteNoUnusedProps = []|[{
527+
checkImportedTypes?: boolean
528+
ignorePatterns?: string[]
529+
}]
520530
// ----- svelte/no-useless-mustaches -----
521531
type SvelteNoUselessMustaches = []|[{
522532
ignoreIncludesComment?: boolean

0 commit comments

Comments
 (0)