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 であることが一目でわかります。isAdult・hasPermission・canEdit・shouldRefresh のような形が理想です。条件変数が特に効果的な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 文の使い方もあわせてご覧ください。
