概要
状態フラグとは「true/falseを表す変数」ではない。
それは**“制御の分岐点を表す記号であり、設計意図の複雑性を反映した構造的トリガー”**である。
複雑化したフラグ管理、入り組んだif文、深いネスト。
これらは往々にして**「仕様ではなくコードの都合によって」生まれる構造的負債**である。
本稿では、状態フラグをどう定義し、if分岐をどう構造的に整理するかという
“脱ネスト・脱フラグ”設計戦略について解説する。
1. 状態フラグの罠:フラグが増えるほどコードが壊れる
if (isAdmin && !isSuspended && hasAccess) {
renderDashboard();
}
- ✅ 単純な論理だが、3つの真偽値の組み合わせは8通り
- ❌ 組み合わせが増えるほど 条件が意図とズレる
2. 意味的な状態を「値」で持つ(フラグをやめる)
// ❌ フラグで状態管理
if (isLoading) ...
if (isError) ...
if (isSuccess) ...
// ✅ 意味をenum/定数で明示
const status = "success"; // "loading" | "error" | "success"
switch (status) {
case "loading": ...
case "error": ...
case "success": ...
}
- ✅ 状態の意味を 「型 + 値」で定義 → 拡張・判定が明確に
- ✅ 条件式ではなく 状態遷移を管理する発想
3. 複雑な条件は「命名関数 or ガード節」で整理する
function canAccess(user) {
return user.isAdmin && !user.isSuspended && user.token !== null;
}
if (!canAccess(currentUser)) return showError();
- ✅ 条件式を「名詞化」することで意図が明示される
- ✅ ネストを浅く保つ構造(ガード節)に変換
4. フラグの「逆」条件は読みやすく書き換える
// ❌ 二重否定で読みづらい
if (!isNotFound) { ... }
// ✅ 肯定表現に置き換える
const isFound = !isNotFound;
if (isFound) { ... }
- ✅ ロジックよりも「人間の読みやすさ」が優先されるべき
- ✅ 否定条件はリファクタ対象
5. 状態管理にEnum的構造を導入する(TypeScriptにも好相性)
const STATUS = {
LOADING: "loading",
SUCCESS: "success",
ERROR: "error"
};
switch (status) {
case STATUS.LOADING:
case STATUS.SUCCESS:
case STATUS.ERROR:
...
}
- ✅ 文字列のハードコーディングを避ける
- ✅ 状態追加時も意図が見える・崩れにくい
設計判断フロー
① フラグが3つ以上同時に存在しないか?(論理爆発リスク)
② 条件文の長さが「3つ以上の論理結合」になっていないか?
③ 否定条件(!isX)の多用で読みにくくなっていないか?
④ 状態の意味がフラグではなく「状態名」として表現できないか?
⑤ 条件式を「名前付きの関数 or 命名された変数」にできないか?
よくあるミスと対策
❌ if (a && !b && c)
のような無名の論理評価
→ ✅ canActivate() のような意味を持つ関数に変換する
❌ フラグを3つ並列で管理して状態の整合性が壊れる
→ ✅ 状態を「ひとつの値(enum, status文字列)」として定義する
❌ ネストが深く、else if地獄になっている
→ ✅ ガード節(先にreturn)で条件ごとに切り出す
結語
状態フラグとは「制御用の真偽値」ではない。
それは**“状態の流れを示す構造化された情報”**であり、
その表現が適切でなければ、コードの読みやすさと保守性は急速に劣化する。
- フラグではなく「状態名」として意味を表現し
- if条件は「意図を名前にする」構造で管理し
- ネストや否定を減らすために設計で整理する
JavaScriptにおけるフラグと分岐の設計とは、
“状態の意味を正しく名前づけ、構造としてコードに表すための論理設計戦略”である。