JavaScript Tips | 基本・共通ユーティリティ:安全処理 – undefined 防止代入

JavaScript JavaScript
スポンサーリンク

「undefined 防止代入」とは何を守りたいのか

「undefined 防止代入」は、一言でいうと
「よく分からない値を代入したせいで、ちゃんと入っていた値を undefined で上書きしてしまう事故を防ぐ」ためのテクニックです。

業務コードでよくあるのが、こんなパターンです。

let state = { pageSize: 20 };

const input = {}; // ユーザー入力。pageSize が入っていない

state.pageSize = input.pageSize; // ← ここで undefined が代入される
console.log(state.pageSize); // 20 だったのに、undefined になってしまう
JavaScript

「値が来ていれば上書きしたいけど、
undefinednull なら、今の値を壊したくない」

こういう場面で使うのが「undefined 防止代入」です。
“代入する前に、代入していい値かどうかをちゃんと見る”という発想のユーティリティです。


まずは基本:null 合体演算子 ?? を使った「安全な代入」

「undefined / null のときだけ、元の値を残す」

一番シンプルな形は、??(null 合体演算子)を使うパターンです。

let pageSize = 20;

const inputPageSize = undefined;

pageSize = inputPageSize ?? pageSize;

console.log(pageSize); // 20 のまま
JavaScript

a ?? b は「a が null または undefined のときだけ b を使う」という意味です。
これを代入に使うと、

pageSize = inputPageSize ?? pageSize;
JavaScript

はこう読み替えられます。

inputPageSize に“ちゃんと値”が入っていればそれを使う。
null / undefined なら、今の pageSize をそのまま残す。」

もう少しパターンを見てみましょう。

let value = 10;

value = 0 ?? value;          // 0(0 は有効な値)
value = "" ?? value;         // ""(空文字も有効な値)
value = null ?? value;       // value のまま
value = undefined ?? value;  // value のまま
JavaScript

ここでの重要ポイントは、
0 や空文字 "" は「ちゃんとした値」として扱われることです。
業務では「0 も空文字も意味がある」ことが多いので、
|| ではなく ?? を使うのが安全です。


変数だけでなく「オブジェクトのプロパティ」に対する undefined 防止代入

「来ているものだけ上書きしたい」パターン

設定オブジェクトの更新などでよくあるのが、

「ユーザーが指定してきた項目だけ上書きしたい。
指定されていない項目は、今の値をそのまま残したい。」

というパターンです。

const state = {
  pageSize: 20,
  theme: "light",
};

const input = {
  pageSize: 50,
  theme: undefined,
};

state.pageSize = input.pageSize ?? state.pageSize;
state.theme    = input.theme    ?? state.theme;

console.log(state);
// { pageSize: 50, theme: "light" }
JavaScript

ここでやっていることは、

  • pageSize は 50 が来ているので上書き
  • themeundefined なので、元の "light" を維持

という「undefined 防止代入」です。


ユーティリティ関数としての undefined 防止代入

毎回 a = b ?? a と書くのは少し読みにくいので、
「“undefined じゃなければ代入する”関数」として切り出すと、意図がはっきりします。

単一値用:assignIfDefined

function assignIfDefined(currentValue, newValue) {
  return newValue === undefined ? currentValue : newValue;
}
JavaScript

使い方はこうです。

let pageSize = 20;

pageSize = assignIfDefined(pageSize, undefined); // 20 のまま
pageSize = assignIfDefined(pageSize, 50);        // 50 に更新

console.log(pageSize); // 50
JavaScript

ここでのポイントは、
「undefined のときだけ“代入しない”」というルールを関数名に閉じ込めていることです。
読む側が「これは undefined 防止代入なんだな」と一目で分かります。


オブジェクト更新用の undefined 防止マージ

「undefined は無視してマージする」ユーティリティ

設定オブジェクトなどをまとめて更新したいとき、
「undefined のプロパティは無視してほしい」というニーズがよくあります。

const current = {
  pageSize: 20,
  theme: "light",
};

const patch = {
  pageSize: 50,
  theme: undefined,
};
JavaScript

これを、「undefined を無視してマージ」したいわけです。

// 期待する結果
{
  pageSize: 50,   // 上書き
  theme: "light", // undefined は無視
}
JavaScript

そのためのユーティリティを作ってみます。

function mergeWithoutUndefined(target, patch) {
  const result = { ...target };

  if (patch == null) return result;

  for (const key of Object.keys(patch)) {
    const value = patch[key];
    if (value !== undefined) {
      result[key] = value;
    }
  }

  return result;
}
JavaScript

使い方です。

const next = mergeWithoutUndefined(current, patch);
console.log(next);
// { pageSize: 50, theme: "light" }
JavaScript

ここでの重要ポイントは、

  • undefined のプロパティは「なかったもの」として扱う
  • それ以外の値は普通に上書きする

というルールをはっきり決めていることです。
これが「undefined 防止代入」をオブジェクトレベルに拡張した形です。


「undefined を防ぐ」のか「null も防ぐ」のかを決める

ここで一つ、必ず設計で悩むポイントがあります。
それは、

「null は“意図的な空”なのか、“値がない”なのか」

という問題です。

例えば、こういう入力が来たとします。

const current = { title: "デフォルト" };
const patch   = { title: null };
JavaScript

ここで、

  • 「null も“値がない”とみなして、元の値を残す」のか
  • 「null は“意図的に空にした”とみなして、null に上書きする」のか

どちらが正しいかは、業務ルール次第です。

なので、ユーティリティも分けておくと安全です。

undefined だけ防ぐ版

function mergeWithoutUndefined(target, patch) {
  const result = { ...target };
  if (patch == null) return result;

  for (const key of Object.keys(patch)) {
    const value = patch[key];
    if (value !== undefined) {
      result[key] = value;
    }
  }

  return result;
}
JavaScript

null も防ぐ版(nullish 防止)

function mergeWithoutNullish(target, patch) {
  const result = { ...target };
  if (patch == null) return result;

  for (const key of Object.keys(patch)) {
    const value = patch[key];
    if (value !== undefined && value !== null) {
      result[key] = value;
    }
  }

  return result;
}
JavaScript

どちらを使うかは、
「このプロジェクトでは null をどう扱うか」というチームの合意に合わせるのが大事です。


実務での具体的な利用イメージ

フォーム入力から状態を更新するとき

フォームで「一部の項目だけ送られてくる」ケースを考えます。

let user = {
  name: "山田",
  email: "yamada@example.com",
};

const input = {
  name: "山田 太郎",
  email: undefined, // メールアドレスは変更なし
};

user = mergeWithoutUndefined(user, input);

console.log(user);
// { name: "山田 太郎", email: "yamada@example.com" }
JavaScript

ここで undefined 防止代入をしていないと、
emailundefined に上書きされてしまい、「メールアドレスが消える」という事故になります。

設定の上書き(オプション引数)

関数にオプション設定を渡すときも、undefined 防止代入はよく使います。

const defaultOptions = {
  pageSize: 20,
  theme: "light",
};

function createList(options = {}) {
  const finalOptions = mergeWithoutUndefined(defaultOptions, options);
  console.log(finalOptions);
}
JavaScript

こうしておけば、呼び出し側がうっかり theme: undefined を渡しても、
デフォルトの "light" が消えることはありません。


小さな練習で感覚をつかむ

次のようなパターンを自分で試してみてください。

const base = { a: 1, b: 2, c: 3 };

const patches = [
  { a: 10 },
  { b: undefined },
  { c: null },
  { a: 0, b: "", c: false },
];
JavaScript

これに対して、

  • mergeWithoutUndefined
  • mergeWithoutNullish

をそれぞれ適用して、「最終的なオブジェクトがどう変わるか」をコンソールに出してみると、
「undefined を防ぐ」と「null も防ぐ」の違いが体感できます。

ここまで理解できれば、「undefined 防止代入」はもうあなたの武器です。
あとは、実際のプロジェクトの中で、

「ここ、たまに undefined で上書きされてバグるんだよな」

という場所に、少しずつこのパターンを差し込んでいけば、
コードの“壊れにくさ”がじわじわ上がっていきます。

タイトルとURLをコピーしました