【JavaScript】条件判定に変数を使う方法|複雑な条件の分解・フラグ変数・早期リターン・ガード節・可読性を高める実践テクニック

if (user.age >= 18 && user.country === "JP" && !user.isBanned) のような条件式は、1行で書けますが読み解くのに時間がかかります。

このような複雑な条件を意味のある変数名に分解することで、コードを「仕様書のように読める」状態に変えられます。この記事では、条件判定に変数を活用して可読性・保守性を高める実践テクニックを解説します。

スポンサーリンク

複雑な条件式を変数に切り出して読みやすくする

条件をそのまま if 文に書くより、意図を表した変数名に代入してから使うと、コードを読んだだけで「何をチェックしているか」が分かります。

NG: 条件式をそのまま if に書く
// 何をチェックしているか読み解くのに時間がかかる
if (user.age >= 18 && user.country === 'JP' && !user.isBanned && user.role !== 'guest') {
  showPremiumContent();
}
OK: 条件を意味のある変数に分解する
const isAdult       = user.age >= 18;
const isDomestic    = user.country === 'JP';
const isActiveUser  = !user.isBanned;
const isLoggedIn    = user.role !== 'guest';

const canViewPremium = isAdult && isDomestic && isActiveUser && isLoggedIn;

if (canViewPremium) {
  showPremiumContent();
}
条件変数の命名のコツ:is〜has〜can〜should〜 で始まる名前にすると、変数が boolean であることが一目でわかります。isAdulthasPermissioncanEditshouldRefresh のような形が理想です。

条件変数が特に効果的な3つのパターン

パターン①:何度も使う条件を1つにまとめる

重複する条件を変数で一元管理
const isAdmin = user.role === 'admin' || user.role === 'superadmin';

// 1つの変数を複数箇所で使い回せる
if (isAdmin) showAdminMenu();
if (isAdmin) allowDataExport();
if (isAdmin) enableUserManagement();

// NG: 条件をその都度書くと変更漏れのリスクがある
// if (user.role === 'admin' || user.role === 'superadmin') showAdminMenu();
// if (user.role === 'admin' || user.role === 'superadmin') allowDataExport(); ...
DRY(Don’t Repeat Yourself)原則:同じ条件式を複数箇所に書くと、仕様変更のときに全箇所を修正する必要があります。変数に切り出しておけば1箇所の修正で済みます。「superadmin」ロールが追加になったとき、変数定義1行の変更だけで全箇所に反映されます。

パターン②:条件の意味をビジネスロジックで表現する

技術的な条件からビジネスロジックへ
// 技術的な条件をそのまま書くと、後から読んだときに意図が不明瞭
if (items.length > 0 && totalPrice >= 3000 && couponCode !== null && !isHoliday) {
  applyFreeShipping();
}

// 各条件をビジネス用語で命名して意図を明確化
const hasItems          = items.length > 0;
const meetsMinOrder     = totalPrice >= 3000;    // 3000円以上で送料無料
const hasCoupon         = couponCode !== null;
const isEligibleDay     = !isHoliday;            // 祝日は対象外

const qualifiesForFreeShipping = hasItems && meetsMinOrder && (hasCoupon || isEligibleDay);

if (qualifiesForFreeShipping) {
  applyFreeShipping();
}

パターン③:段階的な条件チェック(フェーズ分け)

バリデーションの段階分け
// 段階ごとに変数を作ることで、どの段階で失敗したかも追いやすい
const isFormValid    = form.checkValidity();
const isNetworkOk    = navigator.onLine;
const isNotSubmitting = !isSubmitting;

// 全条件が揃ったときだけ送信可能
const canSubmit = isFormValid && isNetworkOk && isNotSubmitting;

submitBtn.disabled = !canSubmit;

// どの条件が失敗しているか個別に確認できる
if (!isFormValid)     showError('入力内容を確認してください');
if (!isNetworkOk)     showError('オフライン状態です');
if (!isNotSubmitting) showError('送信中です。しばらくお待ちください');

早期リターン(ガード節)で条件のネストを減らす

複数の条件チェックを if/else のネストで書くと深くなりがちです。ガード節(Guard Clause)で無効な状態を先に弾くと、メインロジックがフラットで読みやすくなります。

NG: 深いネスト
function processOrder(order) {
  if (order) {
    if (order.items.length > 0) {
      if (order.user.isVerified) {
        if (!order.isCancelled) {
          // ようやくメインロジック
          chargePayment(order);
          sendConfirmationEmail(order);
        }
      }
    }
  }
}
OK: ガード節で早期リターン
function processOrder(order) {
  // 無効な状態を先に弾く(ガード節)
  if (!order)                 return;
  if (order.items.length === 0) return;
  if (!order.user.isVerified)  return;
  if (order.isCancelled)       return;

  // メインロジックはフラットに書ける
  chargePayment(order);
  sendConfirmationEmail(order);
}
ガード節 + 条件変数の組み合わせ
function processOrder(order) {
  const isValidOrder     = order && order.items.length > 0;
  const isUserVerified   = order?.user?.isVerified;
  const isNotCancelled   = !order?.isCancelled;

  if (!isValidOrder)    return;
  if (!isUserVerified)  return;
  if (!isNotCancelled)  return;

  chargePayment(order);
  sendConfirmationEmail(order);
}

状態を定数変数で管理する(Enum 的パターン)

「”active”・”inactive”・”pending” のような文字列をハードコードしない」ことが保守性の基本です。定数オブジェクトで状態を管理すると、タイポによるバグを防ぎ IDE の補完も効くようになります。

NG: 文字列ハードコード
// 'active' をあちこちで書くと typo が起きやすい
if (user.status === 'actve') { // typo!バグになる
  showDashboard();
}
OK: 定数オブジェクトで状態を管理
// 状態を定数にまとめる(Object.freeze で変更を防ぐ)
const USER_STATUS = Object.freeze({
  ACTIVE:   'active',
  INACTIVE: 'inactive',
  PENDING:  'pending',
  BANNED:   'banned',
});

// 使うときは定数経由(タイポしても JS がエラーを出す)
if (user.status === USER_STATUS.ACTIVE) {
  showDashboard();
}

// 複数状態の一括チェック
const VIEWABLE_STATUSES = [USER_STATUS.ACTIVE, USER_STATUS.PENDING];
const canView = VIEWABLE_STATUSES.includes(user.status);

if (canView) showContent();
TypeScript の enum や Union 型も有効:TypeScript を使う場合は type UserStatus = "active" | "inactive" | "pending" のような Union 型やenum UserStatus { Active = "active", ... } で型安全にできます。純粋な JavaScript では上記の Object.freeze() パターンが最も近い代替です。

switch 文の代替:オブジェクトマップパターン

値によって処理を分岐するとき、switch 文の代わりにオブジェクト(マップ)を使うとよりコンパクトに書けます。

switch 文 vs オブジェクトマップ
// switch 文(長くなりやすい)
function getStatusLabel(status) {
  switch (status) {
    case 'active':   return '有効';
    case 'inactive': return '無効';
    case 'pending':  return '審査中';
    case 'banned':   return '凍結';
    default:         return '不明';
  }
}

// オブジェクトマップ(コンパクト)
const STATUS_LABELS = {
  active:   '有効',
  inactive: '無効',
  pending:  '審査中',
  banned:   '凍結',
};

function getStatusLabel(status) {
  return STATUS_LABELS[status] ?? '不明';
}

// 処理(関数)もマップできる
const ACTIONS = {
  'click':  () => console.log('クリックされました'),
  'hover':  () => console.log('ホバーされました'),
  'submit': () => console.log('送信されました'),
};

function handleEvent(type) {
  const action = ACTIONS[type];
  if (action) action();
}

条件変数はデバッグも容易にする

条件を変数に切り出すと、console.log でそれぞれの状態を確認しやすくなります。

デバッグのしやすさ
const isAdult      = user.age >= 18;
const isDomestic   = user.country === 'JP';
const isActiveUser = !user.isBanned;

// 各条件を個別に確認できる
console.log({ isAdult, isDomestic, isActiveUser });
// { isAdult: true, isDomestic: false, isActiveUser: true }
// → isDomestic が false なのが原因とすぐわかる

const canViewPremium = isAdult && isDomestic && isActiveUser;
console.log('canViewPremium:', canViewPremium); // false
console.log({ isAdult, isDomestic }) のショートハンド:オブジェクトのショートハンドプロパティ記法を使うと、変数名と値をセットで出力できます。console.log("isAdult:", isAdult) より console.log({ isAdult }) のほうが変数名が自動的に表示されて便利です。

よくある質問

Q条件変数を増やしすぎると逆に読みにくくなりませんか?
A条件が単純な場合(if (isActive) など)は変数化する必要はありません。目安として、条件式が2つ以上の論理演算子を含む場合や、同じ条件を複数箇所で使う場合に変数化を検討してください。「変数名を読めば何をチェックしているか即座にわかる」ことが変数化の判断基準です。
Qconst で宣言した条件変数は const でよいですか?
Aはい。条件変数は一度設定したら変えないのが基本なので const が適切です。let を使うと「後から変更するかもしれない」という誤ったシグナルを送ることになります。再代入が必要な場合(フラグを途中で変更するなど)のみ let を使ってください。
Qガード節(早期リターン)を使うと「関数の出口が複数ある」と言われますが?
A「関数の出口は1つにすべき」という考え方(単一出口原則)は歴史的にはありましたが、現代の JavaScript/TypeScript では早期リターンを使ったほうがネストが浅くなり読みやすいとされています。多くのスタイルガイドもガード節を推奨しています。深いネストのほうが「この if が最後まで続くのか」を追うのが大変です。
QObject.freeze() を使わないとオブジェクトマップは変更できてしまいますか?
Aはい。Object.freeze() なしでは USER_STATUS.ACTIVE = "changed" のような変更が可能です。Object.freeze() を使うと以降の変更は無視(またはエラー)されます。TypeScript では const STATUS = { ... } as const で読み取り専用にできます。
Q関数の引数を条件変数として使うことはできますか?
Aできます。むしろ引数名が条件の意味を表していれば、変数化と同様の効果が得られます。function canView(isLoggedIn, isVerified, hasPermission) のように引数名で条件を明示することで、呼び出し側のコードも読みやすくなります。

まとめ

条件判定に変数を活用するポイントをまとめます。

  • 複雑な条件式は is〜/has〜/can〜 で始まる意味のある変数名に分解する
  • 同じ条件を複数箇所で使うなら変数化してDRY(重複排除)を実現する
  • ガード節(早期リターン)でネストを浅くし、メインロジックをフラットに保つ
  • 状態値(”active” など)は Object.freeze() で定数化してタイポを防ぐ
  • switch 文はオブジェクトマップで置き換えるとコンパクトになる
  • 条件変数は console.log({ isAdult, isDomestic }) で個別デバッグがしやすくなる

if 文の詳しい使い方は【JavaScript】if 文による条件分岐の書き方、switch 文の使い方は【JavaScript】switch 文の使い方もあわせてご覧ください。