Why You Should Never Assign Undefined in JavaScript Code

Learn why manually assigning undefined to variables is considered bad practice in JavaScript. Discover the problems it causes and the better alternatives professional developers use.

JavaScriptbeginner
8 min read

JavaScript has a simple convention that many developers break without realizing the consequences: undefined is meant to be assigned by the language itself, never by the programmer. When you write let x = undefined or user.name = undefined, you are fighting against the language's design intent and creating code that is harder to read, debug, and serialize.

Think of undefined as JavaScript's way of saying "I have not been given a value yet." When you manually assign undefined, you are telling JavaScript "I am deliberately giving this the value that means it has not been given a value." That contradiction confuses both the runtime and anyone reading your code, because the signal for "intentionally empty" should be null, not undefined.

This guide explains every problem that manual undefined assignment causes and provides the better patterns that experienced developers use instead.

The Core Problem: Ambiguity

When undefined appears in your code, it should mean one of two things: a variable that was declared but never assigned, or a property that does not exist on an object. Manual assignment destroys this semantic clarity:

javascriptjavascript
const user = {
  name: "Alice",
  email: undefined  // Did we deliberately clear the email, or did we forget to set it?
};
 
// Later in the code, another developer reads this:
if (user.email === undefined) {
  // Is the email missing from the data source?
  // Was it deliberately cleared by business logic?
  // Is there a bug where we forgot to assign it?
  // No way to tell.
}

Compare this with using null:

javascriptjavascript
const user = {
  name: "Alice",
  email: null  // Explicitly: this user has no email on file
};
 
if (user.email === null) {
  // Clear: the email was deliberately set to "no value"
}

With null, the intent is unambiguous. With undefined, you are left guessing.

Problem 1: JSON Serialization Drops undefined

When objects are serialized to JSON (APIs, localStorage, sessionStorage, postMessage), properties with undefined values silently disappear:

javascriptjavascript
const formData = {
  name: "Alice",
  middleName: undefined,  // Intended: "no middle name"
  age: 30
};
 
const json = JSON.stringify(formData);
console.log(json); // '{"name":"Alice","age":30}'
// middleName is GONE. Not null, not empty string. Just gone.
 
const parsed = JSON.parse(json);
console.log("middleName" in parsed); // false
console.log(parsed.middleName);      // undefined (but for different reasons now)

Using null preserves the property:

javascriptjavascript
const formData = {
  name: "Alice",
  middleName: null,  // Preserved in JSON as null
  age: 30
};
 
const json = JSON.stringify(formData);
console.log(json); // '{"name":"Alice","middleName":null,"age":30}'
 
const parsed = JSON.parse(json);
console.log("middleName" in parsed); // true
console.log(parsed.middleName);      // null
Data Loss in API Communication

If your backend expects certain fields to be present (even if empty), assigning undefined causes those fields to vanish from the request payload. This can trigger validation errors, default-value bugs, or silent data corruption on the server side.

Problem 2: Default Parameters Do Not Trigger for null

JavaScript default parameters trigger when the argument is undefined but not when it is null. If you deliberately assign undefined thinking it will "reset" a parameter to its default, you might be right, but anyone reading the function call will not understand your intent:

javascriptjavascript
function createProfile(name, bio = "No bio provided") {
  return { name, bio };
}
 
// Passing undefined: triggers default (but why not just omit the argument?)
console.log(createProfile("Alice", undefined));
// { name: "Alice", bio: "No bio provided" }
 
// Passing null: does NOT trigger default
console.log(createProfile("Alice", null));
// { name: "Alice", bio: null }
 
// Just omit it: clearest option
console.log(createProfile("Alice"));
// { name: "Alice", bio: "No bio provided" }

If your intent is "use the default value," omit the argument. If your intent is "explicitly no value," use null.

Problem 3: The in Operator Cannot Distinguish

With manually assigned undefined, the in operator behaves differently from a truly missing property, but the values look identical:

javascriptjavascript
const config = {
  debug: undefined,  // Deliberately set to undefined
  // verbose is not set at all
};
 
console.log(config.debug);   // undefined
console.log(config.verbose); // undefined (looks the same!)
 
// But "in" can tell the difference
console.log("debug" in config);   // true (property exists, value is undefined)
console.log("verbose" in config); // false (property doesn't exist)
 
// This creates confusing behavior in loops
Object.keys(config); // ["debug"] — includes the undefined property

This inconsistency leads to bugs in configuration merging, form handling, and API response processing where code treats "exists but undefined" differently from "does not exist."

Problem 4: typeof Cannot Distinguish Either

The typeof operator returns "undefined" for both undeclared variables and variables explicitly set to undefined:

javascriptjavascript
let assigned = undefined;
// neverDeclared is not declared
 
console.log(typeof assigned);      // "undefined"
console.log(typeof neverDeclared); // "undefined" (same result!)

If typeof is your safety check for "does this variable exist," manually assigning undefined makes that check meaningless.

Problem 5: Object.assign and Spread Behavior

When merging objects, undefined values overwrite existing values in confusing ways:

javascriptjavascript
const defaults = {
  theme: "dark",
  language: "en",
  fontSize: 16
};
 
const userPrefs = {
  theme: "light",
  language: undefined,  // User didn't set this
  fontSize: 14
};
 
// Spread: undefined DOES overwrite the default
const merged = { ...defaults, ...userPrefs };
console.log(merged.language); // undefined (lost the default "en"!)
 
// With null, the behavior is the same (still overwrites)
// But the INTENT is clearer, and you can handle it:
const userPrefsClean = {
  theme: "light",
  language: null,  // Explicitly: "use whatever the system decides"
  fontSize: 14
};
 
// Now you can filter before merging
function mergeWithDefaults(defaults, overrides) {
  const cleaned = {};
  for (const [key, value] of Object.entries(overrides)) {
    if (value !== undefined) {
      cleaned[key] = value;
    }
  }
  return { ...defaults, ...cleaned };
}
 
console.log(mergeWithDefaults(defaults, userPrefs).language); // "en"

What to Use Instead

ScenarioBad (undefined)Good Alternative
Clearing a variableuser = undefineduser = null
Optional object property{ email: undefined }Omit the property entirely
Resetting form fieldfield.value = undefinedfield.value = "" or field.value = null
Function default triggerfn(undefined)Omit the argument: fn()
API empty field{ data: undefined }{ data: null }
Removing an object propertyobj.prop = undefineddelete obj.prop

Clearing a Variable: Use null

javascriptjavascript
// Bad: ambiguous intent
let selectedItem = getItem();
selectedItem = undefined; // Was this intentional or a bug?
 
// Good: clear intent
let selectedItem = getItem();
selectedItem = null; // Deliberately cleared

Removing an Object Property: Use delete

javascriptjavascript
const user = { name: "Alice", temporary: true };
 
// Bad: property still exists with confusing value
user.temporary = undefined;
console.log("temporary" in user); // true (still there!)
Object.keys(user); // ["name", "temporary"]
 
// Good: property is actually removed
delete user.temporary;
console.log("temporary" in user); // false
Object.keys(user); // ["name"]

Signaling "Not Yet Loaded": Use null

javascriptjavascript
// Bad
const [data, setData] = useState(undefined);
// Is data undefined because it hasn't loaded, or because the API returned nothing?
 
// Good
const [data, setData] = useState(null);
// null clearly means "not loaded yet"
// When loaded, data becomes the actual value (even if that value is an empty array)

ESLint Rules That Catch This

Several ESLint rules help enforce the "never assign undefined" convention:

javascriptjavascript
// eslint rule: no-undefined
// Disallows the use of undefined as an identifier
 
// eslint rule: no-undef-init
// Disallows initializing variables to undefined
 
// Bad (caught by no-undef-init)
let name = undefined;
 
// Good
let name;  // Already undefined by default, no need to say it
 
// eslint rule: no-void
// Prevents using void 0 as a way to produce undefined
jsonjson
{
  "rules": {
    "no-undef-init": "error",
    "no-undefined": "warn",
    "init-declarations": ["error", "always", { "ignoreForLoopInit": true }]
  }
}

The One Exception: void 0

In very old JavaScript (pre-ES5), undefined was not a reserved word and could be overwritten:

javascriptjavascript
// This was possible in old engines (no longer a concern)
var undefined = "surprise!";
console.log(undefined); // "surprise!"

Some legacy codebases use void 0 to guarantee a true undefined value. In modern JavaScript (ES5+), undefined is a read-only property of the global object, so void 0 is unnecessary. If you see it in old code, it is safe to replace with undefined in comparisons, but you still should not assign it to variables.

Best Practices

Clean Code Conventions

These patterns keep your codebase consistent and eliminate the ambiguity that manual undefined assignment creates.

Use null for every "intentionally empty" value. Whether it is a cleared form field, an unselected dropdown option, or a not-yet-loaded data state, null communicates intent clearly. Reserve undefined for JavaScript's own "not yet assigned" semantics.

Use delete to remove object properties. Setting a property to undefined leaves a ghost property that shows up in Object.keys() and for...in loops. delete obj.prop actually removes the property from the object.

Omit arguments instead of passing undefined. If you want a function to use its default parameter, simply omit the argument. Explicitly passing undefined works but obscures intent.

Configure ESLint to enforce the convention. Enable no-undef-init and no-undefined rules so your team catches manual undefined assignments during development, not in code review or production.

Document your team's null/undefined convention. Add a section to your project's contributing guide that specifies when to use null vs. when to let undefined occur naturally. Consistency across the codebase matters more than which specific convention you choose.

Common Mistakes and How to Avoid Them

Watch Out for These Pitfalls

These patterns appear frequently in real codebases, especially during refactoring or when handling API responses.

Initializing variables with undefined. Writing let x = undefined is redundant because let x already makes x undefined. The explicit assignment adds noise and suggests the developer does not understand JavaScript's default behavior.

Using undefined to "clear" API request fields. When building request payloads, undefined fields vanish during JSON serialization. If your API needs to distinguish between "field not sent" and "field explicitly empty," use null for the latter.

Confusing undefined with "intentionally removed". If a function returns undefined, it might mean the function has no return statement, the computation produced no result, or there is a bug. Returning null explicitly signals "I computed a result, and that result is nothing."

Checking === undefined when == null would be safer. If you need to handle both null and undefined the same way (which is common), use value == null. It catches both without catching other falsy values like 0 or empty string.

Next Steps

Audit your codebase for manual undefined assignments

Search your project for = undefined assignments and evaluate each one. Replace with null, delete, or omit the assignment as appropriate.

Configure ESLint rules

Add no-undef-init and no-undefined to your ESLint configuration. Run the linter to see how many violations exist and fix them incrementally.

Review the undefined vs null differences

Make sure you understand all the behavioral differences between undefined and null so you can choose the right one in every situation.

Practice with JSON serialization

Create an object with a mix of null and undefined properties, stringify it, parse it back, and observe which properties survive the round-trip. This exercise makes the data loss problem concrete.

Rune AI

Rune AI

Key Insights

  • Never assign undefined: use null for intentional emptiness and delete for removing properties
  • JSON drops undefined: properties set to undefined vanish during serialization; null is preserved
  • Default parameters: only trigger for undefined, so pass null when you want explicit "no value" without triggering defaults
  • Use ESLint: enable no-undef-init and no-undefined rules to enforce the convention automatically
  • Consistency matters: document your team's null/undefined convention and apply it uniformly across the codebase
RunePowered by Rune AI

Frequently Asked Questions

Is it ever acceptable to assign undefined in JavaScript?

In production code, no. There is no scenario where assigning undefined is better than the alternatives (null, delete, or omitting the assignment). Some developers use it in tests to simulate uninitialized state, but even there, simply not assigning the variable achieves the same result. The convention exists because undefined has a specific semantic meaning in JavaScript ("not yet assigned"), and manually assigning it contradicts that meaning.

What is the difference between let x and let x = undefined?

Functionally, they are identical. Both create a variable with the value `undefined`. The difference is intent: `let x` says "I will assign this later," while `let x = undefined` says "I am deliberately setting this to the not-yet-assigned value," which is contradictory and confusing. Use `let x` and save the explicit assignment for when you have an actual value.

Should I use null or an empty string for missing text?

It depends on context. Use `null` when the value is genuinely absent (e.g., a user has not provided a middle name). Use an empty string (`""`) when the value exists but is empty (e.g., a cleared text input field). The distinction matters for validation, display logic, and API semantics. `null` means "no data," while `""` means "data is an empty string."

Does delete actually remove a property from an object?

Yes. `delete obj.prop` removes the property entirely. After deletion, `"prop" in obj` returns false and `Object.keys(obj)` no longer includes it. This is different from `obj.prop = undefined`, which leaves the property key present but sets its value to undefined. The deleted property behaves as if it was never defined.

Why can undefined be overwritten in old JavaScript?

Before ES5 (2009), `undefined` was a mutable property of the global object. Any code could write `undefined = "something"` and change its value, which broke all `=== undefined` checks. ES5 made `undefined` a read-only, non-configurable property of the global object. In modern engines, attempting to overwrite it silently fails in non-[strict mode](/tutorials/programming-languages/javascript/javascript-strict-mode-use-strict-explained) and throws in strict mode.

Conclusion

Manually assigning undefined in JavaScript creates ambiguity, causes data loss during JSON serialization, leaves ghost properties in objects, and contradicts the language's design intent. Using null for intentional emptiness, delete for property removal, and simply omitting assignments for uninitialized variables gives you code that is clearer, safer, and more predictable. The convention is simple: let JavaScript own undefined, and use null when you need to say "nothing."