Skip to content

refactor(preferences): tabBehavior/arrowBehaviorの検証をsatisfies由来のヘルパーに統合#75

Merged
hashrock merged 1 commit into
mainfrom
claude/blissful-brown-qn49yt
Jul 22, 2026
Merged

refactor(preferences): tabBehavior/arrowBehaviorの検証をsatisfies由来のヘルパーに統合#75
hashrock merged 1 commit into
mainfrom
claude/blissful-brown-qn49yt

Conversation

@hashrock

Copy link
Copy Markdown
Owner

観測した問題

editorPreferences.tsloadPreferencestabBehavior / arrowBehavior の許容リテラルを === チェーンで独自に列挙して判定しており、EditorPreferences の型定義と手動同期する必要がありました。

tabBehavior:
  parsed.tabBehavior === "indent" || parsed.tabBehavior === "insert-child"
    ? parsed.tabBehavior
    : DEFAULT_PREFERENCES.tabBehavior,

このため、将来 tabBehavior / arrowBehavior にメンバーを追加しても、この検証コードは無言でコンパイルが通ってしまいます。新しい値は localStorage から読み込まれるたびに黙って握りつぶされ、常にデフォルト値へフォールバックし続けるバグの温床でした。

同じ穴は以前 domain/model.tsNodeType / NumFormat にもあり、#69 (refactor(model): NodeType/NumFormatの検証をsatisfies由来のヘルパーに統合) で isStoredNodeType / isNumFormat という as const satisfies Record<...> ベースのヘルパーに置き換えて塞いでいます。editorPreferences.ts は同じリファクタが適用されないまま残っていました。

変更内容

STORED_NODE_TYPE_SET / isStoredNodeType と同じ手筋で、TAB_BEHAVIOR_SET / isTabBehaviorARROW_BEHAVIOR_SET / isArrowBehavior を追加しました。

const TAB_BEHAVIOR_SET = {
  indent: true,
  "insert-child": true,
} as const satisfies Record<EditorPreferences["tabBehavior"], true>;

function isTabBehavior(value: unknown): value is EditorPreferences["tabBehavior"] {
  return typeof value === "string" && value in TAB_BEHAVIOR_SET;
}

loadPreferences はこの判定関数を呼ぶだけにしました。挙動は変えていません。

守りたい設計意図

  • 「型に新しいメンバーを追加したら、それを消費する側の検証コードがコンパイルエラーで教えてくれる」という不変条件を、model.ts の NodeType/NumFormat と同じ形で editorPreferences.ts にも適用する。
  • localStorage は信頼できない入力(別バージョンの保存値、手編集)なので、検証漏れは実行時に静かに劣化する形で現れる。それを型チェック時のエラーに変えることが目的。

確認方法

  • npx tsc --noEmit -p tsconfig.json — 型エラーなし
  • pnpm test (app/application/editorPreferences.test.ts) — 既存のフォールバック挙動テストが全てパス。加えて tabBehavior/arrowBehavior の全リテラルを型で列挙して round-trip する回帰テストを追加(メンバーの typo/rename があればテストがコンパイルエラーになる)。
  • 手動確認: TAB_BEHAVIOR_SET / ARROW_BEHAVIOR_SET から意図的に1メンバーを削除すると satisfies Record<...> の型エラーが発生することを確認済み(コミットには含めていません)。

レビュー観点

  • TAB_BEHAVIOR_SET / ARROW_BEHAVIOR_SET のキーが EditorPreferences の型と一致しているか(satisfies が保証するので実質コンパイラ任せで良い)。
  • loadPreferences の分岐ロジック自体は変更していないこと(振る舞いの同一性)。
  • テストで追加した round-trip が既存の「フォールバック」テストと重複しすぎていないか。

🤖 Generated with Claude Code

https://claude.ai/code/session_01ExGyoV6xYJdhe8QHEfSsnG


Generated by Claude Code

loadPreferencesはtabBehavior/arrowBehaviorの許容リテラルを`===`チェーンで
独自に列挙しており、EditorPreferencesの型定義と手動同期する必要があった。
メンバーを追加してもここは無言でコンパイルが通り、新しい値がlocalStorage
から黙って握りつぶされ続ける穴になっていた(#69でmodel.tsのNodeType/
NumFormatに対して塞いだのと同じ穴)。

isTabBehavior/isArrowBehaviorを追加し、`as const satisfies Record<...>`で
型からメンバー漏れ・過不足をコンパイルエラーにする。loadPreferencesは
その判定関数を呼ぶだけにした。挙動は変えていない。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ExGyoV6xYJdhe8QHEfSsnG
@hashrock
hashrock merged commit b752476 into main Jul 22, 2026
@hashrock
hashrock deleted the claude/blissful-brown-qn49yt branch July 22, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants