「undefined 防止代入」とは何を守りたいのか
「undefined 防止代入」は、一言でいうと
「よく分からない値を代入したせいで、ちゃんと入っていた値を undefined で上書きしてしまう事故を防ぐ」ためのテクニックです。
業務コードでよくあるのが、こんなパターンです。
let state = { pageSize: 20 };
const input = {}; // ユーザー入力。pageSize が入っていない
state.pageSize = input.pageSize; // ← ここで undefined が代入される
console.log(state.pageSize); // 20 だったのに、undefined になってしまう
JavaScript「値が来ていれば上書きしたいけど、undefined や null なら、今の値を壊したくない」
こういう場面で使うのが「undefined 防止代入」です。
“代入する前に、代入していい値かどうかをちゃんと見る”という発想のユーティリティです。
まずは基本:null 合体演算子 ?? を使った「安全な代入」
「undefined / null のときだけ、元の値を残す」
一番シンプルな形は、??(null 合体演算子)を使うパターンです。
let pageSize = 20;
const inputPageSize = undefined;
pageSize = inputPageSize ?? pageSize;
console.log(pageSize); // 20 のまま
JavaScripta ?? 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 が来ているので上書きthemeはundefinedなので、元の"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;
}
JavaScriptnull も防ぐ版(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 防止代入をしていないと、email が undefined に上書きされてしまい、「メールアドレスが消える」という事故になります。
設定の上書き(オプション引数)
関数にオプション設定を渡すときも、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これに対して、
mergeWithoutUndefinedmergeWithoutNullish
をそれぞれ適用して、「最終的なオブジェクトがどう変わるか」をコンソールに出してみると、
「undefined を防ぐ」と「null も防ぐ」の違いが体感できます。
ここまで理解できれば、「undefined 防止代入」はもうあなたの武器です。
あとは、実際のプロジェクトの中で、
「ここ、たまに undefined で上書きされてバグるんだよな」
という場所に、少しずつこのパターンを差し込んでいけば、
コードの“壊れにくさ”がじわじわ上がっていきます。
