Aikido

条件式での代入を避ける:隠れたバグを防ぐ

読みやすさ

ルール
禁止事項 配置 割り当て 代入を 条件式の中に代入してはいけない。 
混合 代入  条件 ロジック を作る コード エラーを起こしやすい
そして 難しくする 理解しにくくなる。 理解しにくくなる。 別々の 課題  論理的 チェックを分離する。 

対応言語 言語 JavaScript、 TypeScript、 Python、 PHP

はじめに

条件文の中の代入演算子は、コンパイラーやリンターが見逃しがちなバグの原因です。典型的な間違いは、if文の中で==や===(比較)の代わりに==(代入)を使ってしまうことですが、問題はもっと深いところにあります。条件文の中の意図的な代入でさえ、読みにくく、レビューしにくく、デバッグしにくいコードを作ります。代入と評価が同じ行で行われる場合、読者はどちらの処理が優先されるのか、また実際にテストされるのはどの値なのかを頭の中で解析しなければなりません。

なぜそれが重要なのか

なぜそれが重要なのか

バグの紹介: タイプミスによる変更 === への = を指定しても構文エラーにはなりません。条件は比較結果ではなく、代入された値(真偽値)に対して評価されます。

コードの可読性:読者は、条件文が値を変更するのではなく、値をテストすることを期待している。この両方が同時に起こった場合、メンテナはどの変数がいつ変更されたかを追跡しなければならない。

コード例

非準拠:

function processUser(userData) {
    if (user = userData.user) {
        console.log(`Processing user: ${user.name}`);
        return user.id;
    }
    return null;
}

function validateInput(value) {
    if (result = value.match(/^\d{3}-\d{2}-\d{4}$/)) {
        return result[0];
    }
    return false;
}

なぜ間違っているのか:条件式の中に代入があるため、意図的なのかタイプミスなのかが不明確になっている。最初の例は====を意図したバグかもしれないし、2番目の例は正規表現マッチングと代入が混在しているため、コードの流れがわかりにくくなっている。

✅ 準拠:

function processUser(userData) {
    const user = userData.user;
    if (user) {
        console.log(`Processing user: ${user.name}`);
        return user.id;
    }
    return null;
}

function validateInput(value) {
    const result = value.match(/^\d{3}-\d{2}-\d{4}$/);
    if (result) {
        return result[0];
    }
    return false;
}

なぜこれが重要なのか: 代入を条件から切り離すことで、意図が明確になる。読者はすぐに ユーザー が最初に抽出され、次にテストされる。正規表現にマッチした結果はキャプチャされ、その後評価される。曖昧さがなく、認知的なオーバーヘッドもない。 ==== が明らかになった。

結論

代入を条件式から分離しておくことは、単純なルールであり、バグの一群を防ぐことができる。結合された操作を解析することによる認知的なオーバーヘッドは、認識される簡潔さの利点を上回ります。代入と評価が別個の操作である明確で明示的なコードは、可読性を向上させ、バグを減らし、コード・レビューをより効果的にします。

よくある質問

ご質問は?

ファイルの読み込みのように、条件文の代入が慣用的な場合はどうだろう?

while (line = file.readline())が一般的な言語であっても、現代のベストプラクティスは明示的な分離を好む。JavaScriptでは、イテレータ・プロトコルを使う。Python 3.8+では、セイウチ演算子:=は、条件式での代入が本当に必要な場合に、その意図を明示します。

代入と条件分岐を分離することによるパフォーマンスへの影響はありますか?

いいえ、最近のJavaScriptエンジンは、両方のパターンを同じように最適化します。分離によって変数宣言が1つ追加されますが、コンパイル後の実行時コストはゼロです。パフォーマンスの違いは、バグ防止や可読性の利点に比べればごくわずかです。まず明確なコードを書き、プロファイリングで実際のボトルネックが特定されたときだけ最適化する。

if ((match = regex.exec(str)) !== null)のようなパターンはどう扱えばいいのでしょうか?

これを2つの文に分割する: const match = regex.exec(str); if (match !== null).あるいは、次のような現代的な選択肢を使うこともできる。match()は失敗するとnullを返すので、明示的なnullチェックは冗長になります。明快さが増し、意図が明白になります。

意図的に戻り値を使用する代入についてはどうだろうか?

意図的であることは良い習慣を意味しない。条件分岐の戻り値の代入に依存するコードは、メンテナンス上の危険を引き起こす。将来の編集者は、タイプミスのように見えるものを「修正」するかもしれない。どうしてもこのパターンを使わなければならない場合は、理由を説明するコメントを追加してください。

このルールは三項演算子にも適用されますか?

はい。const x = (y = getValue()) ? y : defaultValueを避ける。これはif文よりも読みにくい。const y = getValue(); const x = y ? y : defaultValue.あるいは、ヌル併合を使うのがよいだろう: const x = getValue() ? defaultValue.最近の演算子は、このような厄介なパターンを避けるために存在する。

リンターや静的解析ツールはこのパターンをどう扱うのか?

最近のリンターのほとんどは、デフォルトか設定によって条件分岐に代入のフラグを立てる。一般的に、意図的な代入を知らせるために余分な括弧if ((x = y))を必要としますが、これはコードのにおいです。リンターの例外を無効にし、コードを適切に修正する方が良い。静的解析ツールは、CI/CD中にこれらのパターンを検出し、本番環境に到達するのを防ぐことができる。

まずは無料で体験

コード、クラウド、ランタイムを1つの中央システムでセキュアに。
脆弱性を迅速に発見し、自動的に修正。

クレジットカードは不要。