TL;DR: Type coercion is when JavaScript silently converts a data type before doing a comparison or operation. == triggers coercion — 0 == '' is true. === never converts — 0 === '' is false. AI tools mostly generate === now, but older patterns still slip through. Knowing the difference stops you from chasing ghosts in your own code.

Why This Bites Vibe Coders Specifically

Here is a scenario that plays out constantly. You ask your AI to build a form that checks whether a user entered a quantity. The AI generates this:

const quantity = document.getElementById('qty').value;

if (quantity == 0) {
  alert("Please enter a quantity!");
}

Looks reasonable. But this code has a bug. If the user leaves the field blank, quantity is an empty string (''). And in JavaScript, '' == 0 evaluates to true. So your "please enter a quantity" alert fires even when the user hasn't touched the field yet.

The fix is one character: change == to ===. But you have to know to look for it.

That is type coercion in the wild. It is not an error. It is not a crash. It is JavaScript being "helpful" in a way that quietly breaks your logic — and the AI generated it because it learned from older codebases where == was common.

What Is a "Type" First?

Before coercion makes sense, you need to understand that every value in JavaScript has a data type. The main ones you will hit every day:

  • Number42, 3.14, 0, -7
  • String'hello', "42", '' (empty string)
  • Booleantrue or false
  • Null — explicitly nothing. A developer set this to nothing on purpose.
  • Undefined — nothing because it was never set. A variable that exists but has no value.

Notice that 42 (number) and "42" (string) are different types even though they look similar. JavaScript knows the difference — most of the time. The problem is what happens when you compare them.

Loose Equality (==) vs Strict Equality (===)

JavaScript gives you two comparison operators. They look almost identical. They behave very differently.

Strict Equality: === (Three Equals)

This is what you want almost every time. Three equals signs mean: "Are these the same value AND the same type?" If they are different types, the answer is immediately false — no conversion, no tricks:

// === never converts types
42 === 42        // true  — same value, same type
42 === "42"      // false — number vs string, done
0 === false      // false — number vs boolean
'' === false     // false — string vs boolean
null === undefined // false — different types entirely

Strict equality is predictable. If you put a number in, you get a number comparison. If you put a string in, you get a string comparison. No surprises.

Loose Equality: == (Two Equals)

Two equals signs mean: "Are these equal — and if not, try converting them first." JavaScript runs a set of conversion rules and then compares. The results range from sensible to baffling:

// == converts types before comparing
42 == "42"       // true  — string "42" becomes number 42
0 == false       // true  — false becomes 0
0 == ''          // true  — empty string becomes 0
'' == false      // true  — both become 0
null == undefined // true  — special rule, just because
null == 0        // false — null only equals null/undefined
NaN == NaN       // false — NaN is never equal to anything, even itself

These results are not random — JavaScript follows specific rules. But those rules are complex enough that almost no one memorizes them correctly. The most practical advice: use === and avoid needing to know the rules at all.

The Rule of Thumb

Use === by default. Every time. The only widely accepted exception is value == null, which intentionally checks for both null and undefined at once. Outside that one case, there is almost never a good reason to reach for ==.

The Coercion Gotchas Table

Here are the comparisons that trip up developers and AI-generated code most often. Each one uses == and returns a result that surprises most people:

Expression Result Why it Surprises People
'' == 0 true Empty string coerces to 0
'0' == false true "0" → 0, false → 0
null == undefined true Special case rule in the spec
null == 0 false null only equals null/undefined
[] == false true Empty array coerces to "" then 0
[0] == false true [0] → "0" → 0, false → 0
NaN == NaN false NaN is not equal to anything, ever
false == '' true Both coerce to 0

The strict equality versions of all of these are false, which is what you almost certainly intended. That table is the argument for always using ===.

Truthy and Falsy: The Other Side of Coercion

Type coercion does not only happen in comparisons. It also happens anywhere JavaScript expects a boolean — like inside an if statement. Every value is either truthy or falsy.

The complete list of falsy values in JavaScript:

// These six values are falsy — they behave like false
false
0
''        // empty string
null
undefined
NaN

// Everything else is truthy, including:
1           // any non-zero number
'hello'     // any non-empty string
'0'         // the string "0" is truthy!
[]          // empty array is truthy
{}          // empty object is truthy
-1          // negative numbers are truthy

This is where a lot of real bugs in AI-generated code hide. Look at this conditional:

// AI-generated code to show user data
function displayUserName(name) {
  if (name) {
    document.getElementById('greeting').textContent = 'Hello, ' + name;
  } else {
    document.getElementById('greeting').textContent = 'Hello, Guest';
  }
}

Seems fine. But what if name is '0'? Some systems use "0" as a placeholder or default value. '0' is truthy, so the greeting shows "Hello, 0" instead of "Hello, Guest." The AI's logic worked for normal names but not for edge-case values.

What if name is 0 (a number, not a string)? That is falsy, so the user gets "Hello, Guest" even though a value was technically there.

The fix is to be explicit about what you are checking:

// Explicit check: did we get a non-empty string?
function displayUserName(name) {
  if (typeof name === 'string' && name.trim().length > 0) {
    document.getElementById('greeting').textContent = 'Hello, ' + name;
  } else {
    document.getElementById('greeting').textContent = 'Hello, Guest';
  }
}

Now the check only passes if name is actually a string with content. No surprises from truthy/falsy coercion.

Real Bugs in AI-Generated Code: Three Scenarios

Scenario 1: Form Input vs Number

This is the most common real-world coercion trap. All values from HTML form inputs are strings — always. Even if a user types a number, you get a string back. AI sometimes forgets this:

// Bug: form input is a string, score is a number
const score = 100; // number from your app
const inputScore = document.getElementById('score-input').value; // string!

if (inputScore == score) {
  // This passes! "100" == 100 is true due to coercion
  // But is that what you want?
  alert("Scores match!");
}

// Better:
const inputScoreNumber = Number(inputScore);
if (inputScoreNumber === score) {
  alert("Scores match!");
}

The == version "works" in the happy path but hides the fact that you are comparing mismatched types. What happens when you need to add the values? "100" + score gives you "100100" (string concatenation), not 200.

Scenario 2: The Null vs Undefined Mix-Up

APIs return data in inconsistent shapes. Sometimes a field is null. Sometimes it is undefined. AI-generated code sometimes treats them as interchangeable — which works with == but can cause problems when the distinction matters:

// API response might have userId as null (field exists, no value)
// or userId as undefined (field is missing entirely)
const userId = apiResponse.userId;

// This check treats both null and undefined as "no user"
if (userId == null) {
  // runs for both null and undefined
  redirectToLogin();
}

// If you need to distinguish between "field missing" vs "field empty":
if (userId === undefined) {
  console.error("API response is missing userId field");
} else if (userId === null) {
  redirectToLogin(); // User exists but not logged in
}

The == null pattern is actually one case where experienced developers intentionally use == — it catches both null and undefined in one line. But if you need to tell them apart for error handling, you need ===.

Scenario 3: Array Checks Gone Wrong

AI often generates code to check if something is "empty." Arrays behave unexpectedly with coercion:

// Bug: empty array check with ==
const items = [];

if (items == false) {
  // This runs! [] coerces to "" which coerces to 0
  // 0 == false is true
  console.log("No items!"); // Wrong — items exists, just empty
}

// What you actually want:
if (items.length === 0) {
  console.log("No items!");
}

// Or for a general "is this a populated array":
if (Array.isArray(items) && items.length > 0) {
  console.log("Has items:", items);
}

Prompt to Audit Your Code

Review this code for type coercion bugs. Find every
place == is used and tell me if it should be ===.
Also check if statements that rely on truthy/falsy
and flag any that could behave unexpectedly.

[paste your code here]

Coercion Happens in Operations Too, Not Just Comparisons

Coercion is not limited to ==. JavaScript also converts types during arithmetic and string operations. This shows up constantly in AI-generated code that processes form data:

// The + operator does string concatenation if either side is a string
const a = "5";
const b = 3;

a + b   // "53" — not 8! String concatenation wins
a - b   // 2 — subtraction has no string version, so JS converts
a * b   // 15 — same, multiplication forces number conversion
a / b   // 1.666... — division forces number conversion

// Why this matters in real code:
const price = document.getElementById('price').value;   // "29.99" (string)
const qty   = document.getElementById('qty').value;     // "3" (string)

const total = price * qty;   // 89.97 — works! Both coerce to numbers
const tax   = price + 2.50;  // "29.992.5" — WRONG! + concatenates strings

// Fix: always convert first
const totalSafe = Number(price) * Number(qty);  // 89.97
const taxSafe   = Number(price) + 2.50;         // 32.49

The subtraction/multiplication/division operators force number conversion. But + is ambiguous — it means both "add numbers" and "join strings." When any value is a string, it joins instead of adds. AI-generated code that does math on form inputs will hit this sooner or later.

Why AI Sometimes Generates ==

Modern AI coding tools (Cursor, GitHub Copilot, Claude) have largely learned to generate === by default. But you will still see == appear in these situations:

  • Translating from other languages. Python's == is strict. Java's == compares references. When AI translates logic from another language, it might carry over the == habit even though JavaScript == behaves differently.
  • Older training data. The internet contains decades of JavaScript code. Pre-2010 code almost universally uses ==. AI models trained on this code absorb the pattern.
  • The null check exception. Because value == null is a legitimately useful pattern, AI sometimes generalizes it and uses == in other places too.
  • Autocomplete in loose contexts. When AI generates quick utility functions or one-liners, it sometimes defaults to the shorter form.

How to Catch It Automatically

ESLint has a rule called eqeqeq that flags every use of == in your code. Turn it on and it will catch coercion bugs before they reach production — including in AI-generated code you paste in. The rule has a "null" option that allows == null while banning all other uses of ==.

TypeScript Solves This Permanently

The real fix for type coercion bugs is TypeScript. TypeScript is JavaScript with types declared upfront. When you declare that a variable is a number, TypeScript will warn you if you try to compare it to a string — before the code ever runs:

// TypeScript would catch this at compile time
const price: number = 29.99;
const input: string = document.getElementById('price').value;

if (price === input) {
  // TypeScript error: "This condition will always return false
  // since the types 'number' and 'string' have no overlap."
}

// TypeScript forces you to convert explicitly:
if (price === Number(input)) {
  // OK — both are numbers now
}

TypeScript does not eliminate every coercion scenario, but it eliminates the most common category: comparing values of different types. AI-generated TypeScript code has significantly fewer coercion bugs than plain JavaScript code for this reason.

If you are building anything beyond a quick prototype, TypeScript is worth the setup cost. Ask your AI: "Set this project up with TypeScript" and it will configure everything.

What to Tell Your AI

Knowing about coercion lets you write more precise prompts. Here are the most useful ones:

Enforce Strict Equality

Review this code and replace every == with ===
unless there's a specific reason to use ==. Explain
any cases where you leave == intentionally.

Fix Form Input Math

This code reads values from form inputs and does
math with them. Convert all input values to numbers
before any arithmetic operations. Use Number() or
parseFloat() as appropriate.

Make Conditionals Explicit

Audit the if statements in this code. Anywhere we
rely on truthy/falsy checks for non-boolean values,
replace with explicit comparisons (=== null,
=== undefined, .length === 0, etc.).

Set Up ESLint Rules

Add the eqeqeq rule to our ESLint config. Use the
"smart" option that allows == null but bans all
other uses of ==.

Quick Reference: The Rules That Matter

If you remember nothing else from this article, remember these five things:

  1. Use === by default. Every comparison. No exceptions except == null.
  2. Form inputs are always strings. Convert to Number before doing math: Number(input) or parseFloat(input).
  3. Empty string, 0, null, undefined, and NaN are all falsy. If your if check is supposed to catch only one of these, use === to be explicit.
  4. + concatenates if either side is a string. Subtract, multiply, or divide to force numeric conversion — or use Number() explicitly.
  5. NaN is never equal to anything, including itself. Use Number.isNaN(value) to check for it.

What to Learn Next

Type coercion connects to several other fundamentals worth understanding:

  • What Is a Data Type? — the types that JavaScript is converting between. Understanding types makes coercion predictable.
  • What Are Conditionals? — where truthy/falsy coercion lives. if statements implicitly convert to boolean.
  • What Is a Variable? — variable declarations affect whether coercion surprises are possible.
  • What Is Error Handling? — coercion bugs often surface as silent errors rather than crashes, which makes them especially hard to catch without error handling.
  • What Is ESLint? — the tool that catches coercion issues automatically. Set it up once and it watches for == in every file forever.
  • What Is TypeScript? — the permanent solution. TypeScript prevents entire categories of coercion bugs at the type level.

Next Step

Open any AI-generated JavaScript file and search for == (not ===). Every match is a potential coercion bug. Fix them by adding a third equals sign and see if any of your tests break — if they do, you just found a real bug your AI quietly introduced.

FAQ

Type coercion is when JavaScript automatically converts a value from one type to another during a comparison or operation. For example, when you compare a number and a string with ==, JavaScript converts one to match the other before comparing. This happens silently — no error, no warning — which is what makes it dangerous.

== is loose equality — it converts types before comparing. 1 == '1' is true because JavaScript converts the string '1' to the number 1. === is strict equality — it never converts types. 1 === '1' is false because one is a number and the other is a string, full stop. Use === in almost every situation.

AI models are trained on massive amounts of code from the internet, including older code that predates the community-wide shift to ===. AI tools have mostly learned to default to ===, but they can still generate == when translating logic from other languages, when writing quick comparisons, or when working with legacy patterns. Always review equality checks in AI-generated code.

Every value in JavaScript is either truthy or falsy — meaning it behaves like true or false in a boolean context. Falsy values are: false, 0, '' (empty string), null, undefined, and NaN. Everything else is truthy. This matters because type coercion uses truthy/falsy logic, and conditionals implicitly convert values to booleans.

The most common dangerous pattern is comparing form input values (always strings) to numbers with == or using them directly in arithmetic with +. A close second is using == to check for null or undefined in places where distinguishing between the two matters. Use === and explicit type conversion to avoid both.

This is the one widely-accepted exception to the === rule. Writing value == null catches both null and undefined in one check, which is intentional and readable. Some style guides and senior developers allow this specific pattern. However, for beginners and AI-generated code, it is safer to check explicitly: value === null || value === undefined. ESLint's eqeqeq rule has a "null" option that permits this exception.