Recommended resources
The freely available resources this lesson will direct you to.
Hello! We’ll begin the JavaScript and TypeScript interview module with a skill that shows up in both output-prediction questions and production bugs: working out what JavaScript does when values have different types. Your experience with Node and React gives us a practical setting—form inputs and query parameters often arrive as strings even when they represent numbers.
By the end of this lesson, you should be able to predict results involving conversion, ==, ===, and truthiness, and explain why each result occurs. The next lesson will shift from values to execution order in the event loop.
One value, several possible conversions
Consider a count entered into a form:
const rawCount = "0";
rawCount is a string, not a number. What happens next depends on the operation, not just the value. JavaScript might convert it to a number, test its truthiness, or leave it as a string. These are different operations with different rules.
Start with a short demonstration of the bug that results from treating those operations as interchangeable.
Watch This If You Don’t Understand Type Coercion
Watch “Watch This If You Don’t Understand Type Coercion” from Web Dev Simplified to see why a valid count of zero can be mistaken for missing input.
Watch the form bug, then the Boolean conversion behind !count. Finish with implicit versus explicit conversion. Focus on the distinction between zero is falsy and zero was not supplied.
A truthiness test converts a value to a Boolean. It happens in an if condition and when you apply !. The falsy values you are most likely to encounter are false, 0, -0, 0n, NaN, "", null, and undefined. Other values are truthy—including "0", "false", [], and {}. A string does not become false merely because its text says “false.”
console.log(Boolean("0")); // true
console.log(Boolean(0)); // false
console.log(Boolean("")); // false
console.log(Boolean([])); // true
console.log(!0); // true
If you are used to Python, the empty-array result is worth noticing: an empty Python list is falsy, but an empty JavaScript array is truthy.
Numeric conversion is a separate question. Number("0") is 0, while Boolean("0") is true. Number("") is also 0; Number("not a count") is NaN. Converting first can therefore change which branch runs:
const rawCount = "0";
const count = Number(rawCount);
console.log(Boolean(rawCount)); // true
console.log(Boolean(count)); // false
Operators can request different conversions too. With +, a string operand can cause concatenation. Subtraction instead requests numbers:
console.log("5" + 1); // "51"
console.log("5" - 1); // 4
console.log(Number("5") + 1); // 6
For an interview output question, avoid the vague shortcut “JavaScript converts strings to numbers.” Ask which operator is being evaluated and what conversion that operator requests.
Equality has its own rules
The comparison operators == and === answer different questions. Strict equality, ===, does not convert one operand to another type. Loose equality, ==, can perform conversions—but it does not simply convert every pair of values to numbers.
Watch “JavaScript == VS ===” from Web Dev Simplified for a quick visual contrast between the operators.
Watch the comparisons, from a number versus a string through zero, an empty string, and the null/undefined case. Notice which results change when the operator changes. Treat the advice to prefer === as a coding default; the reading below gives the more precise rules needed to predict ==.
The MDN guide is the reference to use when a case is less obvious than the video examples.
Equality comparisons and sameness - JavaScript | MDN
Read MDN’s “Equality comparisons and sameness” for the strict-equality rule and the ordered cases used by loose equality.
In “Strict equality using ===”, read the type rule. In the same section, read the edge cases to see how NaN and signed zero behave. Then, in “Loose equality using ==”, follow the numbered rules from rule 1 through the “Number to String” bullet and its conversion-failure sentence. Focus on same-type comparisons, the special null/undefined case, Boolean-to-number conversion, and string-to-number conversion; you can leave the subsequent BigInt cases for another time.
Here is a compact way to apply those rules:
- For
===, different types meanfalse. With matching types, compare the values; objects compare by identity rather than contents. - For
==with matching types, comparison works much as it does for===. No conversion is needed. - For
==with different types, check for a special rule before assuming numeric conversion.nullandundefinedequal each other under==, but neither loosely equals0orfalse. A Boolean compared with another type is converted to0or1. A string compared with a number is converted to a number.
For example, false == "0" is true: false becomes numeric 0, then "0" becomes numeric 0. In contrast, null == 0 is false. Although Number(null) evaluates to 0, the loose-equality rule for null does not use that numeric conversion. This is why memorizing Number(value) results alone cannot solve every equality question.
console.log("5" == 5); // true
console.log("5" === 5); // false
console.log(0 == false); // true
console.log(0 === false); // false
console.log(null == undefined); // true
console.log(null === undefined); // false
console.log(null == 0); // false
Two further cases matter in interviews. First, objects are compared by reference, not by a visual comparison of their contents:
const first = { id: 1 };
const alias = first;
console.log(first === alias); // true: the same object
console.log(first === { id: 1 }); // false: a different object
console.log([1] == [1]); // false: two different arrays
Second, NaN is not equal to itself under either equality operator:
console.log(NaN === NaN); // false
console.log(NaN == NaN); // false
console.log(Number.isNaN(NaN)); // true
For application code, === is generally the clearer default. A deliberate value == null can be useful when you specifically mean “either null or undefined”; it does not mean “any falsy value.”
Truthiness is not equality
It is tempting to read if (value) as if (value == true). Those expressions are not equivalent. An if condition applies Boolean conversion; == follows the equality rules above.
An empty array makes the distinction unusually clear:
console.log(Boolean([])); // true
console.log([] == false); // true
The first line tests whether the array is truthy; all ordinary arrays are. In the second line, loose equality first converts false to 0. For this comparison, the empty array converts to an empty string, which then converts to numeric 0. The surprising pair of results follows from two different conversions, not a contradiction.
The logical operators || and && also use truthiness to decide what to do, but they return an operand, not necessarily a Boolean. For ||, a truthy left operand is returned; otherwise the right operand is returned. For &&, a falsy left operand is returned; otherwise the right operand is returned.
console.log("0" || "missing"); // "0"
console.log(0 || "missing"); // "missing"
console.log(0 && "saved"); // 0
That makes || useful for some defaults but dangerous when zero is valid. If you need a default only for null or undefined, the nullish-coalescing operator ?? expresses that narrower intention:
console.log(0 || 10); // 10
console.log(0 ?? 10); // 0
The practical question is not “Is this value falsy?” but “Which values should count as missing for this field?”
Trace an output from left to right
Suppose a React input or URL query parameter supplies "0". The following snippet combines the lesson’s three kinds of reasoning:
const raw = "0";
const count = Number(raw);
console.log(raw + 1); // "01"
console.log(count + 1); // 1
console.log(raw == 0); // true
console.log(raw === 0); // false
console.log(Boolean(raw)); // true
console.log(Boolean(count)); // false
console.log(count || 10); // 10
The first two lines of output depend on what + receives: a string or a number. The equality lines use the same original string but different comparison operators. The Boolean lines show that the string "0" and number 0 take different truthiness paths. Finally, || returns the fallback because the converted count is falsy.
When predicting output, a reliable method is to write down each operand’s current value and type, identify the operator, apply that operator’s conversion rule, and only then determine what gets printed. Keep raw and count distinct: converting a value into a new variable does not change the type of the original.
In production code, handle a count according to its actual input contract. For a string input, check blank text before converting; otherwise Number("") would turn missing input into a seemingly valid zero:
function readCount(raw) {
if (raw.trim() === "") {
return { error: "Enter a count" };
}
const count = Number(raw);
if (!Number.isFinite(count)) {
return { error: "Enter a valid number" };
}
return { value: count };
}
console.log(readCount("0")); // { value: 0 }
console.log(readCount("")); // { error: "Enter a count" }
This separates three questions that a single if (!count) cannot answer: Was text supplied? Can it be converted to a finite number? Is zero an acceptable value?
Key takeaways
JavaScript does not have one universal coercion rule. Truthiness, numeric conversion, string concatenation, and loose equality operate in different contexts. Prefer === when you want equality without implicit type conversion, and explain == by following its rules rather than guessing. Above all, remember that a valid numeric zero is falsy, while the string "0" is truthy.
Next, we’ll use the same careful tracing approach to predict execution order across the call stack, microtask queue, and task queue.