---
title: correctness/stale-prop-derivation · Stale prop derivation
description: $derived を使わずに prop から計算した値は一度しか評価されず、気づかないうちに親の変更へ追従しなくなります。
---

**重大度:** warning · **カテゴリ:** correctness

## チェック内容

`$props()` の prop から `$derived` なしで計算され、テンプレートで描画されるトップレベルの `const`/`let` を検出します。

```svelte
<script>
  let { type } = $props();

  // 検出対象 — 初回レンダリングの値で固定される
  let color = type === 'danger' ? 'red' : 'green';
</script>

<p class={color}>...</p>
```

検出は意図的に保守的で、次をすべて満たすときだけ対象になります。

- 初期化子が **eager な位置**で prop を参照している。関数・アロー関数・getter の中の参照はリアクティブなままなので数えません。
- 初期化子が関数呼び出し・`new`・`await` を含まない。このため `$state(initial)` によるキャプチャ、`$derived`、サービスの構築は構造的に対象外です。
- 束縛が再代入も受け渡しもされない。
- 実際にテンプレートで描画されている。イベントハンドラーの中でしか使われない束縛は数えません。

## なぜ重要か

Svelte のガイダンスは、props を変わるものとして扱うよう求めています。`$derived` を使わない素の代入は初期化時に一度だけ評価されるため、初回マウントでは正しく描画されますが、その後は親の変更に追従しなくなります。コンパイラも svelte-check も警告しないため、レビューをすり抜けて本番で発覚しがちな stale-UI バグです。

## 修正方法

```svelte
<script>
  let { type } = $props();

  let color = $derived(type === 'danger' ? 'red' : 'green');
</script>
```

関数本体が必要な計算には `$derived.by(() => ...)` を使ってください。一度きりのスナップショットが本当に必要な場合（非制御コンポーネントの初期値など）は、`let value = $state(initialValue)` が公式パターンで、これは検出対象になりません。

### legacy mode（`export let`）

同じバグは legacy mode のコンポーネントにも存在し、修正方法だけが異なります。Svelte は 1 つのファイル内で `export let` と `$props()` を混在できないため、このルールは両方の prop 記法を認識し、メッセージを出し分けます。

```svelte
<script>
  export let type;

  // 検出対象 — 初回レンダリングの値で固定される
  let color = type === 'danger' ? 'red' : 'green';
</script>
```

```svelte
<script>
  export let type;

  $: color = type === 'danger' ? 'red' : 'green';
</script>
```

代入の前に `$:`（リアクティブ文）を付けることが、legacy mode における `$derived` の等価物です。`type` が変わるたびに再実行されるようになり、初期化時の一度きりの評価ではなくなります。

## 制限事項

関数呼び出しを含む式を対象外とするため、メソッドを使った派生（`type.toUpperCase()`、`items.filter(...)`）は現時点では検出されません。精度を優先した意図的なトレードオフで、将来のバージョンで純粋な組み込みメソッドが allow-list に加わる可能性があります。

親がその prop を実際に変えるかは静的には分かりませんが、変えない場合でも `$derived` のコストはゼロです。`correctness/unmutated-state` との関係にも注意してください。prop から計算され一度も書き込まれない `$state` の正しい修正は、`const` ではなく `$derived` です。

## モードによる違い

ありません。このルールが読むのは同じ `.svelte` / `.ts` のソースファイルなので、CLI、Vite プラグインのビルド、ライブダッシュボードの静的ベースラインのいずれでも結果は同一で、レンダリング済み HTML の解析で再評価されることもありません。`--route` で実行範囲を絞ると、このルールは動きません。コンポーネントスコープのルールには、検出を紐づけるルートが無いためです。

## 無効化

```js svelte-vitals.config.js
export default {
  rules: {
    'correctness/stale-prop-derivation': 'off'
  }
};
```
