December Code

JavaScript type coercion interview questions, with answers

JavaScript converts values between types without being asked, and that is where most of its interview questions come from. An interviewer writes an addition or a comparison between a string and a number, an empty array and false, or an object and a number, and asks what it gives. The answers follow a small set of rules — which operator converts to which type, and what each type becomes — so every answer below states the rule and then shows it in code. Every output shown was produced by running the code in Node.js.

The questions start with the types themselves, then equality and truthiness, then the conversions that arithmetic performs. Then take the free JavaScript diagnostic — ten questions across every JavaScript topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.What are the data types in JavaScript?

    In short: Seven primitive types — string, number, bigint, boolean, undefined, symbol and null — plus objects, which include arrays, functions, dates and everything else.

    Primitives are immutable values compared by value; objects are mutable and compared by reference, so two separate arrays with the same contents are never equal to each other. typeof reports a value's type with two historical quirks: typeof null is "object", a bug kept for compatibility, and a function reports "function" even though functions are objects. Arrays are objects too, so Array.isArray is the reliable test for one. number is a 64-bit float for every numeric value, so integers are exact only up to Number.MAX_SAFE_INTEGER, 2 to the power 53 minus 1, as the last line below shows; bigint, written with an n suffix, handles larger integers but cannot be mixed with numbers in arithmetic.

    console.log(typeof 42n);
    // bigint
    console.log(typeof Symbol());
    // symbol
    console.log(2 ** 53 + 1);
    // 9007199254740992
  2. 2.What is the difference between == and === in JavaScript?

    In short: === compares without converting, so values of different types are never equal; == first converts the operands towards a common type, which is where its surprises come from.

    Strict equality is true only for the same type and value, with two exceptions: NaN is not equal to itself, and +0 equals -0. Loose equality converts first: a string compared with a number becomes a number, a boolean becomes a number before anything else, and an object is converted to a primitive, so [] == false is true by way of "" and 0. null and undefined are loosely equal to each other and to nothing else, which makes x == null the one widely accepted use of ==: it tests for both at once, as below. Everywhere else, use ===. Object.is is stricter still: it treats NaN as equal to NaN and +0 as different from -0.

    console.log("1" == 1);
    // true
    console.log("1" === 1);
    // false
    console.log([] == false);
    // true
    let x;
    console.log(x == null);
    // true
  3. 3.Which values are falsy in JavaScript?

    In short: Eight values: false, 0, -0, 0n, "", null, undefined and NaN; every other value, including "0", "false", [] and {}, is truthy.

    An if, a while, !, && and || all convert their operand to a boolean, and that conversion has a short list of false results. Everything not on the list is truthy, and the surprises are values that look empty: the string "0", the string "false", an empty array and an empty object are all truthy, so if (arr) does not test for an empty array; test arr.length instead. The practical trap is treating 0 or "" as missing: a default written with || replaces a legitimate zero or empty string, which is what the ?? operator was added to fix. Double negation, !!value, or Boolean(value) converts a value to its boolean, which is how the map below works.

    const v = [0, "0", [], NaN];
    const b = v.map(Boolean);
    console.log(b.join(" "));
    // false true true false
  4. 4.How does the + operator decide between addition and concatenation?

    In short: + converts both operands to primitives, then concatenates if either is a string and adds numbers otherwise; the other arithmetic operators always convert to numbers.

    Binary + is the only arithmetic operator with two meanings. It first converts each operand to a primitive, which for arrays and most objects means a string, then concatenates if either result is a string and adds otherwise. -, *, / and % only do arithmetic, so they convert both sides to numbers and produce NaN when that fails; this page's sample shows the difference with one string operand. Evaluation is left to right, so 1 + 2 + "3" adds before it concatenates, while "1" + 2 + 3 concatenates twice. Unary + is the short way to convert a string to a number, though Number(s) says it more clearly. Adding two arrays joins their string forms.

    console.log(1 + 2 + "3");
    // 33
    console.log("1" + 2 + 3);
    // 123
    console.log([1] + [2]);
    // 12
    console.log(+"42" + 1);
    // 43
  5. 5.What is NaN in JavaScript, and how do you test for it?

    In short: NaN is the number value for a failed numeric result and the only value not equal to itself; test for it with Number.isNaN, which, unlike the global isNaN, does not convert its argument first.

    Operations such as 0 / 0, Math.sqrt(-1) or Number("12px") produce NaN, and typeof NaN is "number", as below. Because NaN compares unequal to everything, including itself, x === NaN is always false. The global isNaN converts its argument to a number first, so it reports true for any string that is not numeric and for undefined, which is rarely what the code meant. Number.isNaN, added in ES2015, returns true only for the NaN value itself. NaN also spreads: any arithmetic with a NaN operand gives NaN, which is why a NaN deep in a calculation usually traces back to one bad parse, and why parsed input deserves a check at the point it enters the program.

    const n = Number("12px");
    console.log(n, typeof n);
    // NaN number
    console.log(isNaN(undefined));
    // true
    console.log(
      Number.isNaN(undefined));
    // false
  6. 6.How does JavaScript convert objects and arrays to primitives?

    In short: It calls Symbol.toPrimitive if the object defines it, otherwise valueOf and toString in an order set by the hint, which is why an array usually becomes a comma-joined string.

    When an operator needs a primitive, the object is asked for one. With a number hint, as for - or *, valueOf is tried first and toString second; with a string hint, as in a template literal, the order is reversed; + and == use a default hint that behaves like number for everything except Date. Plain objects and arrays return themselves from valueOf, so toString decides: an array joins its elements with commas, giving "" for [] and "1,2" for [1, 2], and a plain object gives "[object Object]". That explains [] + {} and why [] == 0 is true. Defining valueOf or Symbol.toPrimitive changes the conversion, as the price object below does.

    const price = {
      valueOf() { return 50; },
    };
    console.log(price * 2);
    // 100
    console.log(`${[1, 2]}`);
    // 1,2
    console.log([] + {});
    // [object Object]
  7. 7.Why is 0.1 + 0.2 not equal to 0.3 in JavaScript?

    In short: Numbers are 64-bit binary floats, so 0.1 and 0.2 are stored approximately and their sum is 0.30000000000000004; compare with a tolerance, or work in whole units such as paise.

    JavaScript has one numeric type for integers and fractions alike: an IEEE 754 double. Decimal fractions such as 0.1 have no exact binary form, so each is rounded to the nearest representable value, and the rounding errors show once values are combined. This is not specific to JavaScript: every language that uses IEEE 754 doubles gives the same sum. For display, toFixed(2) rounds to a string. For comparison, test whether the difference is below a small tolerance, as below. For money, keep amounts as integers in the smallest unit, paise rather than rupees, and format them only for display. Integers are exact only up to Number.MAX_SAFE_INTEGER; beyond that, use BigInt.

    const s = 0.1 + 0.2;
    console.log(s);
    // 0.30000000000000004
    console.log(s.toFixed(2));
    // 0.30
    const ok =
      Math.abs(s - 0.3) < 1e-9;
    console.log(ok);
    // true

How the diagnostic asks it

One question from the JavaScript bank, exactly as a sitting would show it. The bank has 4 on types & coercion and 30 across JavaScript.

Types & Coercion · easyJS-003

What does this code print?

console.log("5" + 3, "5" - 3, "5" * "2");
  1. 18 2 10
  2. 253 2 10correct
  3. 353 NaN NaN
  4. 453 2 52

+ with a string operand concatenates, so "5" + 3 is "53". The other arithmetic operators have no string meaning, so they convert both operands to numbers: "5" - 3 is 2 and "5" * "2" is 10. 8 2 10 treats + as addition. 53 NaN NaN assumes strings cannot take part in arithmetic, but numeric strings convert cleanly. 53 2 52 assumes * repeats the string, which it does in Python, not JavaScript.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 JavaScript questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated