correctness/stale-prop-derivation · Stale prop derivation
$derived を使わずに prop から計算した値は一度しか評価されず、気づかないうちに親の変更へ追従しなくなります。
重大度: warning · カテゴリ: correctness
チェック内容
$props() の prop から $derived なしで計算され、テンプレートで描画されるトップレベルの const/let を検出します。
<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 バグです。
修正方法
<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 記法を認識し、メッセージを出し分けます。
<script>
export let type;
// 検出対象 — 初回レンダリングの値で固定される
let color = type === 'danger' ? 'red' : 'green';
</script>
<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 で実行範囲を絞ると、このルールは動きません。コンポーネントスコープのルールには、検出を紐づけるルートが無いためです。
無効化
export default {
rules: {
'correctness/stale-prop-derivation': 'off'
}
};