Skip to content

feat: add no-unused-props rule #1061

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/twelve-beers-talk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'eslint-plugin-svelte': minor
---

feat: add `no-unused-props` rule
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ These rules relate to better ways of doing things to help you avoid problems:
| [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: |
| [svelte/no-svelte-internal](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-svelte-internal/) | svelte/internal will be removed in Svelte 6. | :star: |
| [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 | |
| [svelte/no-unused-props](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-props/) | Warns about defined Props properties that are unused | :star: |
| [svelte/no-unused-svelte-ignore](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/) | disallow unused svelte-ignore comments | :star: |
| [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: |
| [svelte/no-useless-mustaches](https://sveltejs.github.io/eslint-plugin-svelte/rules/no-useless-mustaches/) | disallow unnecessary mustache interpolations | :star::wrench: |
Expand Down
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mts can be ts now.

File renamed without changes.
1 change: 1 addition & 0 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ These rules relate to better ways of doing things to help you avoid problems:
| [svelte/no-reactive-literals](./rules/no-reactive-literals.md) | don't assign literal values in reactive statements | :star::bulb: |
| [svelte/no-svelte-internal](./rules/no-svelte-internal.md) | svelte/internal will be removed in Svelte 6. | :star: |
| [svelte/no-unused-class-name](./rules/no-unused-class-name.md) | disallow the use of a class in the template without a corresponding style | |
| [svelte/no-unused-props](./rules/no-unused-props.md) | Warns about defined Props properties that are unused | :star: |
| [svelte/no-unused-svelte-ignore](./rules/no-unused-svelte-ignore.md) | disallow unused svelte-ignore comments | :star: |
| [svelte/no-useless-children-snippet](./rules/no-useless-children-snippet.md) | disallow explicit children snippet where it's not needed | :star: |
| [svelte/no-useless-mustaches](./rules/no-useless-mustaches.md) | disallow unnecessary mustache interpolations | :star::wrench: |
Expand Down
208 changes: 208 additions & 0 deletions docs/rules/no-unused-props.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
---
pageClass: 'rule-details'
sidebarDepth: 0
title: 'svelte/no-unused-props'
description: 'Warns about defined Props properties that are unused'
---

# svelte/no-unused-props

> Warns about defined Props properties that are unused
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> **_This rule has not been released yet._** </badge>
- :gear: This rule is included in `"plugin:svelte/recommended"`.

## :book: Rule Details

This rule reports properties that are defined in Props but never used in the component code.
It helps to detect dead code and improve component clarity by ensuring that every declared prop is utilized.

This rule checks various usage patterns of props:

- Direct property access
- Destructuring assignment
- Method calls
- Computed property access
- Object spread
- Constructor calls (new expressions)
- Assignment to other variables
- Index signatures (e.g. `[key: string]: unknown`)

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.

Note: Properties of class types are not checked for usage, as they might be used in other parts of the application.

: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`.

<!--eslint-skip-->

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Direct property access
const props: { value: string } = $props();
console.log(props.value);
</script>
```

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Destructuring assignment
const { width, height }: { width: number; height: number } = $props();
console.log(width, height);
</script>
```

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Class properties are not checked
class User {
constructor(
public name: string,
public age: number
) {}
}
type Props = {
user: User;
};
const props: Props = $props();
console.log(props.user.name); // age is not reported as unused
</script>
```

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Method calls
const props2: { callback: () => void } = $props();
props2.callback();
</script>
```

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Computed property access
const props3: { 'data-value': string } = $props();
const value = props3['data-value'];
</script>
```

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Constructor calls
const props4: { config: { new (): any } } = $props();
new props4.config();
</script>
```

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Using index signature with rest operator
interface Props {
a: number;
[key: string]: unknown;
}
let { a, ...rest }: Props = $props();
console.log(rest);
</script>
```

```svelte
<!-- ✗ Bad Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Unused property 'b'
const props: { a: string; b: number } = $props();
console.log(props.a);
</script>
```

```svelte
<!-- ✗ Bad Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Unused property in destructuring
const { x }: { x: number; y: number } = $props();
console.log(x);
</script>
```

```svelte
<!-- ✗ Bad Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: "error" */
// Unused index signature
interface Props {
a: number;
[key: string]: unknown; // This will be reported
}
let { a }: Props = $props();
</script>
```

## :wrench: Options

```js
{
"svelte/no-unused-props": ["error", {
// Whether to check properties from imported types
"checkImportedTypes": false,
// Patterns to ignore when checking for unused props
"ignorePatterns": []
}]
}
```

- `checkImportedTypes` ... Controls whether to check properties from imported types. Default is `false`.
- `ignorePatterns` ... Patterns to ignore when checking for unused props. Default is an empty array.

Examples:

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: ["error", { "checkImportedTypes": true }] */
// Check properties from imported types
import type { BaseProps } from './types';
interface Props extends BaseProps {
age: number;
}
let { name, age }: Props = $props();
console.log(name, age);
</script>
```

```svelte
<!-- ✓ Good Examples -->
<script lang="ts">
/* eslint svelte/no-unused-props: ["error", { "ignorePatterns": ["^_"] }] */
// Ignore properties starting with underscore
interface Props {
_internal: string;
value: number;
}
let { value }: Props = $props();
console.log(value);
</script>
```

## :gear: Required Configuration

This rule requires `@typescript-eslint/parser` to work. Please refer to the [User Guide](../user-guide.md) for more information.

## :mag: Implementation

- [Rule source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/src/rules/no-unused-props.ts)
- [Test source](https://github.com/sveltejs/eslint-plugin-svelte/blob/main/packages/eslint-plugin-svelte/tests/src/rules/no-unused-props.ts)
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const config: Linter.Config[] = [
'svelte/no-store-async': 'error',
'svelte/no-svelte-internal': 'error',
'svelte/no-unknown-style-directive-property': 'error',
'svelte/no-unused-props': 'error',
'svelte/no-unused-svelte-ignore': 'error',
'svelte/no-useless-children-snippet': 'error',
'svelte/no-useless-mustaches': 'error',
Expand Down
10 changes: 10 additions & 0 deletions packages/eslint-plugin-svelte/src/rule-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ export interface RuleOptions {
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-class-name/
*/
'svelte/no-unused-class-name'?: Linter.RuleEntry<SvelteNoUnusedClassName>
/**
* Warns about defined Props properties that are unused
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-props/
*/
'svelte/no-unused-props'?: Linter.RuleEntry<SvelteNoUnusedProps>
/**
* disallow unused svelte-ignore comments
* @see https://sveltejs.github.io/eslint-plugin-svelte/rules/no-unused-svelte-ignore/
Expand Down Expand Up @@ -512,6 +517,11 @@ type SvelteNoUnknownStyleDirectiveProperty = []|[{
type SvelteNoUnusedClassName = []|[{
allowedClassNames?: string[]
}]
// ----- svelte/no-unused-props -----
type SvelteNoUnusedProps = []|[{
checkImportedTypes?: boolean
ignorePatterns?: string[]
}]
// ----- svelte/no-useless-mustaches -----
type SvelteNoUselessMustaches = []|[{
ignoreIncludesComment?: boolean
Expand Down
Loading