A ghost showed up several times in the previous lesson: '8' + 4 gives '84', 0 behaves as if it were false, and null turns into 0 when you add it. All of that has a name: type coercion. It is the mechanism by which JavaScript automatically converts values from one type to another, and it is —without exaggeration— the source of most of a beginner's most baffling bugs. In this lesson you are going to clear it up completely: when JavaScript converts on its own, how to convert explicitly yourself, which values count as true or false, and why you should always use ===. By the end you will know how to safely handle the data from a task form, which always arrives as text.

Contents

  1. Why type conversion exists
  2. Explicit conversion to a number
  3. Explicit conversion to text
  4. Explicit conversion to a boolean: truthy and falsy
  5. Implicit conversion (coercion)
  6. Comparison operators
  7. == versus ===
  8. Comparing strings and comparing numbers
  9. Comparing objects: the reference trap
  10. Object.is and the edge cases
  11. Practical hygiene rules
  12. Application: validating the data from a task form
  13. Common Mistakes and Tips
  14. Exercises
  15. Conclusion

  1. Why type conversion exists

JavaScript was designed to be forgiving. In 1995 the goal was a language that would not fail on imperfect data: better an odd result than a broken page. So when you ask it for something that does not fit —adding text to a number, for example— it does not complain: it converts what it can and carries on.

There are two kinds of conversion:

Kind Who does it Example
Explicit You, on purpose Number('12')12
Implicit (coercion) JavaScript, on its own '12' * 224

The explicit kind is your ally. The implicit kind is the one you need to know about so you do not fall victim to it.

There are only three possible destinations for a conversion: number, text and boolean. Everything else is a variation on these three.

  1. Explicit conversion to a number

2.1 Number()

It converts the whole value to a number. If it cannot, it returns NaN.

console.log(Number('12'));      // 12
console.log(Number('12.5'));    // 12.5
console.log(Number('  12  '));  // 12  ← surrounding spaces are ignored
console.log(Number(''));        // 0   ← careful! the empty string gives 0
console.log(Number('   '));     // 0   ← spaces only, also 0
console.log(Number('12h'));     // NaN ← the entire value has to be numeric
console.log(Number('high'));    // NaN
console.log(Number(true));      // 1
console.log(Number(false));     // 0
console.log(Number(null));      // 0   ← careful!
console.log(Number(undefined)); // NaN

Two results deserve special attention because they cause real bugs:

  • Number('') is 0, not NaN. If a user leaves the hours field empty, you will get 0 instead of an error warning.
  • Number(null) is 0 but Number(undefined) is NaN. Another historical inconsistency you need to know about.

2.2 parseInt() and parseFloat()

These functions are more permissive: they read the number from the beginning and stop at the first invalid character.

console.log(parseInt('12h'));      // 12   ← it keeps what it understands
console.log(parseInt('12.9'));     // 12   ← it truncates the decimals
console.log(parseFloat('12.9h'));  // 12.9 ← it keeps the decimals
console.log(parseInt('h12'));      // NaN  ← it does not start with a number
console.log(parseInt(''));         // NaN  ← here it does give NaN, not 0

Always use parseInt with base 10 as the second argument. It is a habit inherited from an era when text starting with 0 was read as octal:

console.log(parseInt('12', 10)); // 12 — no ambiguity

Side by side:

Value Number() parseInt() parseFloat()
'12' 12 12 12
'12.9' 12.9 12 12.9
'12h' NaN 12 12
'12 hours' NaN 12 12
'' 0 NaN NaN
' ' 0 NaN NaN
'high' NaN NaN NaN
true 1 NaN NaN
null 0 NaN NaN

Which one to use: Number() when you require the entire value to be a number (the usual case when validating data). parseInt/parseFloat when you want to extract a number from text that carries units ('12px', '8 hours').

2.3 The unary + trick

Putting a + in front of a value converts it to a number, exactly like Number():

const hoursText = '12';
console.log(+hoursText);        // 12
console.log(typeof +hoursText); // 'number'

It is short and widely used, but Number() reads better and cannot be confused with an addition. Recognize it when you see it; write Number().

2.4 Validating a number properly

Putting the above together, this is how you correctly check that a piece of text contains a usable number:

const input = '12.5';
const hours = Number(input);

const isValidNumber = !Number.isNaN(hours) && input.trim() !== '';
console.log(isValidNumber); // true

Both checks are needed: Number.isNaN rules out non-numeric text, and trim() !== '' rules out the empty string, which Number() would happily turn into 0.

  1. Explicit conversion to text

3.1 String()

It converts any value to text. It works with everything, including null and undefined.

console.log(String(12));         // '12'
console.log(String(12.5));       // '12.5'
console.log(String(true));       // 'true'
console.log(String(null));       // 'null'
console.log(String(undefined));  // 'undefined'
console.log(String(NaN));        // 'NaN'
console.log(String([1, 2, 3]));  // '1,2,3'
console.log(String({ a: 1 }));   // '[object Object]'

That last line is a classic: if you ever see [object Object] on screen, you have converted an object to text by accident.

3.2 .toString()

This is a method on the value itself. It does the same as String(), with one critical difference:

console.log((12).toString());     // '12'
console.log(true.toString());     // 'true'

// console.log(null.toString());      // TypeError
// console.log(undefined.toString()); // TypeError

String() is safer because it never throws. .toString() fails on null and undefined, which are precisely the cases where you most need it not to fail.

.toString() does have one exclusive ability: converting to another numeric base.

console.log((255).toString(2));  // '11111111' (binary)
console.log((255).toString(16)); // 'ff' (hexadecimal)

3.3 Template literals

The most natural way to convert to text in everyday code:

const hours = 12;
const message = `${hours}`;
console.log(typeof message); // 'string'

Anything you put inside ${} is converted to text automatically.

Method Safe with null/undefined Readability Recommended use
String(value) Yes High General explicit conversion
value.toString() No High Changing the numeric base
`${value}` Yes Very high Composing messages
value + '' Yes Low Avoid

  1. Explicit conversion to a boolean: truthy and falsy

When JavaScript needs a boolean (in an if, in an &&, in a !), it converts whatever you give it. The values that convert to false are called falsy; everything else is truthy.

4.1 The complete list of falsy values

There are only eight falsy values in JavaScript. They are worth memorizing, because everything else is truthy:

Falsy value Comment
false The boolean false
0 Zero
-0 Negative zero
0n The bigint zero
'' The empty string (also "" and ``)
null
undefined
NaN
console.log(Boolean(false));     // false
console.log(Boolean(0));         // false
console.log(Boolean(''));        // false
console.log(Boolean(null));      // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN));       // false

4.2 The most surprising truthy values

Since everything that is not on the list is truthy, some cases come as a shock:

console.log(Boolean('0'));       // true  ← the TEXT '0' is truthy!
console.log(Boolean('false'));   // true  ← so is the TEXT 'false'
console.log(Boolean(' '));       // true  ← a space is not an empty string
console.log(Boolean([]));        // true  ← an empty array is truthy!
console.log(Boolean({}));        // true  ← so is an empty object
console.log(Boolean(-1));        // true  ← any number other than 0

The two that cause the most trouble in practice:

  • '0' is truthy. If a form returns '0' hours, if (hours) will pass.
  • [] is truthy. To find out whether a list of tags is empty you have to check tags.length === 0, not if (!tags).
const tags = [];

if (tags) {
  console.log('This runs ALWAYS, even when the list is empty');
}

if (tags.length === 0) {
  console.log('This is how you really detect an empty list'); // ← correct
}

  1. Implicit conversion (coercion)

Now that you know how to convert things yourself, let's see when JavaScript does it on its own.

5.1 Coercion to text: +

If either operand of + is a string, the other one is converted to a string.

console.log('Hours: ' + 12);     // 'Hours: 12'
console.log(12 + '');            // '12'
console.log('12' + true);        // '12true'
console.log('12' + null);        // '12null'
console.log('12' + undefined);   // '12undefined'
console.log('12' + [1, 2]);      // '121,2'

5.2 Coercion to a number: the other arithmetic operators

-, *, /, % and ** always convert to a number:

console.log('12' - 4);      // 8
console.log('12' * '2');    // 24
console.log(true + 1);      // 2      ← true converts to 1
console.log(false + 1);     // 1      ← false converts to 0
console.log(null + 5);      // 5      ← null converts to 0
console.log(undefined + 5); // NaN    ← undefined converts to NaN
console.log([] + 1);        // '1'    ← the empty array converts to ''
console.log([5] * 2);       // 10     ← [5] converts to '5' and then to 5

The last four lines are the kind of oddity that made JavaScript famous. Do not memorize them: memorize the conclusion, which is do not mix types.

5.3 The case that breaks real calculations

This is the practical bug you really will run into:

// Data arriving from a form: ALWAYS strings
const task1Hours = '12';
const task2Hours = '8';

console.log(task1Hours + task2Hours);  // '128'  ← wrong!

// Converting before operating
console.log(Number(task1Hours) + Number(task2Hours)); // 20 ← correct

And here is what happens when only one of the values is text:

const ivanHours = 12;         // a number
const luciaHours = '8';       // text (it came from an input)

console.log(ivanHours + luciaHours);   // '128'  ← concatenates
console.log(ivanHours - luciaHours);   // 4      ← subtracts correctly
console.log(ivanHours * luciaHours);   // 96     ← multiplies correctly

The fact that some operators work and others do not is what makes this bug so hard to track down: the program seems to work until it reaches an addition.

  1. Comparison operators

Relational operators always return a boolean:

Operator Name Example Result
> Greater than 12 > 8 true
< Less than 12 < 8 false
>= Greater than or equal 12 >= 12 true
<= Less than or equal 8 <= 12 true
== Loose equality '12' == 12 true
=== Strict equality '12' === 12 false
!= Loose inequality '12' != 12 false
!== Strict inequality '12' !== 12 true
const estimatedHours = 12;
const MAX_DAILY_HOURS = 8;

console.log(estimatedHours > MAX_DAILY_HOURS);  // true
console.log(estimatedHours <= MAX_DAILY_HOURS); // false

  1. == versus ===

Here is the heart of the lesson.

  • === (strict equality) compares value and type. If the types differ, it simply returns false.
  • == (loose equality) converts the values to a common type before comparing.
console.log('12' === 12);  // false ← different types
console.log('12' == 12);   // true  ← it converts '12' to 12 and compares

7.1 The classic == cases

Comparison == === Why
'5' == 5 true false The text is converted to a number
'' == 0 true false The empty string is converted to 0
'0' == 0 true false '0' is converted to 0
'' == '0' false false Both are text: they are compared as they are
false == 0 true false false is converted to 0
false == '' true false Both are converted to 0
null == undefined true false A special rule of the language
null == 0 false false null is only loosely equal to undefined
NaN == NaN false false NaN is never equal to anything, not even itself
[] == false true false []''0, and false0
[] == '' true false The empty array converts to an empty string

Notice the devastating inconsistency: '' == 0 is true and '0' == 0 is true, but '' == '0' is false. In other words, == is not transitive. Two values can both be "equal" to a third one and yet not be equal to each other. That alone should be enough to banish == from your code.

7.2 The one tolerated exception

There is one use of == that many teams accept: checking in one go whether a value is null or undefined.

const reviewer = null;

// With == it covers both cases at once
if (reviewer == null) {
  console.log('No reviewer assigned');
}

// The strict equivalent, more explicit
if (reviewer === null || reviewer === undefined) {
  console.log('No reviewer assigned');
}

Even so, today there is a cleaner alternative with ??, which you already know:

const reviewerName = reviewer ?? 'No reviewer assigned';

7.3 The case of NaN

NaN is the only value in JavaScript that is not equal to itself, neither with == nor with ===:

console.log(NaN === NaN); // false
console.log(NaN == NaN);  // false

That is why Number.isNaN() exists:

const hours = Number('twelve'); // NaN
console.log(Number.isNaN(hours)); // true

Watch out for the old global isNaN() function, which converts before checking and produces false positives:

console.log(isNaN('high'));        // true  ← it converts 'high' to NaN first
console.log(Number.isNaN('high')); // false ← 'high' is a string, not NaN

Always use Number.isNaN().

  1. Comparing strings and comparing numbers

The < and > operators behave differently depending on the types.

If both are text, they are compared character by character according to their Unicode code point (roughly, alphabetical order):

console.log('high' < 'low');    // true  ← 'h' comes before 'l'
console.log('Iván' < 'iván');   // true  ← uppercase comes first
console.log('10' < '9');        // true  ← a TEXT comparison! '1' < '9'

That last line is a classic trap: as text, '10' is less than '9' because only the first character is compared.

If either one is a number, both are converted to numbers:

console.log(10 < 9);      // false
console.log('10' < 9);    // false ← '10' is converted to 10

To sort text according to a language's own rules (accents, special letters) there is localeCompare:

console.log('ñandú'.localeCompare('nutria', 'es')); // 1 → 'ñandú' comes after

8.1 Why ISO dates are so convenient

Here you can see why in Nómada Tasks we store dates as 'yyyy-mm-dd' text:

const date1 = '2026-09-30';
const date2 = '2026-11-05';

console.log(date1 < date2); // true — the correct chronological order

The ISO format has the property that its alphabetical order matches its chronological order, because the components go from largest to smallest magnitude and are zero-padded. With '30/09/2026' this would not work at all.

  1. Comparing objects: the reference trap

Remember from the lesson on types: objects and arrays are handled by reference. When you compare them, JavaScript does not look at the contents: it looks at whether they are the same object in memory.

const tags1 = ['design', 'space'];
const tags2 = ['design', 'space'];

console.log(tags1 === tags2); // false ← same contents, different objects
console.log(tags1 == tags2);  // false ← == does not help either

const tags3 = tags1;
console.log(tags1 === tags3); // true ← it is the same object

The same goes for objects:

const taskA = { id: 1, title: 'Review the invoices' };
const taskB = { id: 1, title: 'Review the invoices' };

console.log(taskA === taskB);       // false
console.log(taskA.id === taskB.id); // true ← comparing primitives, it works
flowchart TD
    A["tags1"] --> M1["Memory: ['design','space']"]
    B["tags2"] --> M2["Memory: ['design','space']"]
    C["tags3"] --> M1
    M1 -.->|"=== M2"| R["false: they are<br/>different areas"]

Practical rule: to compare two tasks, compare their fields (normally the id), never the whole objects with ===. The techniques for comparing contents are covered in JSON and Copying Objects.

  1. Object.is and the edge cases

Object.is() (ES2015) behaves like === except in two cases where === is debatable:

console.log(NaN === NaN);          // false
console.log(Object.is(NaN, NaN));  // true  ← more reasonable

console.log(0 === -0);             // true
console.log(Object.is(0, -0));     // false ← it distinguishes negative zero
Comparison == === Object.is
'12' and 12 true false false
null and undefined true false false
NaN and NaN false false true
0 and -0 true true false
'high' and 'high' true true true

Day to day you will use ===. Object.is is useful when you care about distinguishing NaN or negative zero, situations that are rare but real in numeric work.

  1. Practical hygiene rules

Five rules that will spare you most of the problems in this lesson:

  1. Always use === and !==. No exceptions while you are learning. The code-quality tools you will see in Module 8 will warn you if a == slips through.
  2. Convert explicitly before operating. If a value might be text, apply Number() to it before adding it. Do not rely on coercion.
  3. Do not mix types in the same variable. If hours is a number, let it always be a number.
  4. Check for the absence of a value explicitly. if (value === null) or value ?? fallback, instead of relying on truthy/falsy.
  5. Watch out for 0, '' and []. The first two are falsy even when they are valid data; the third is truthy even when it is empty.
flowchart TD
    A["You receive a value"] --> B{"Where does<br/>it come from?"}
    B -->|"A form, a URL,<br/>storage"| C["It is TEXT,<br/>always"]
    B -->|"Your own code"| D["It already has<br/>the right type"]
    C --> E["Convert it with<br/>Number() or String()"]
    E --> F["Validate it with<br/>Number.isNaN() and trim()"]
    F --> G["Compare with ==="]
    D --> G

  1. Application: validating the data from a task form

Marta fills in a form to create a task in Nómada Tasks. Web form fields always return text, even the numeric and date ones. Let's check the data before using it.

'use strict';

// --- Data exactly as it arrives from a form: ALL strings -----------
const idText = '5';
const titleText = '  Set up the screen-printing workshop  ';
const assigneeText = '';          // it was not filled in
const priorityText = 'medium';
const hoursText = '6';
const dueDateText = '2026-10-15';
const tagsText = 'workshop, training';

// --- Explicit conversion ------------------------------------------
const id = Number(idText);
const title = titleText.trim();     // strips the leftover spaces
const estimatedHours = Number(hoursText);

// --- Validations ---------------------------------------------------

// 1. The id must be a valid, positive number
const idIsValid = !Number.isNaN(id) && id > 0;

// 2. The title cannot be empty once trimmed
const titleIsValid = title !== '';

// 3. The hours: a valid number, greater than 0. Careful: Number('') is 0
const hoursAreValid =
  hoursText.trim() !== '' && !Number.isNaN(estimatedHours) && estimatedHours > 0;

// 4. The priority must be one of the three allowed values
const priorityIsValid =
  priorityText === 'high' ||
  priorityText === 'medium' ||
  priorityText === 'low';

// 5. An ISO date is always 10 characters long and cannot be in the past
const TODAY = '2026-08-18';
const dateIsValid =
  dueDateText.length === 10 && dueDateText >= TODAY;

// 6. The assignee may be missing: we use null on purpose
const assignee = assigneeText.trim() === '' ? null : assigneeText.trim();

// 7. The tags: if the text is empty, an empty list
//    split(', ') splits the text on the separator and returns an array
const tags =
  tagsText.trim() === '' ? [] : tagsText.split(', ');

// --- Overall result -------------------------------------------------
const formIsValid =
  idIsValid && titleIsValid && hoursAreValid &&
  priorityIsValid && dateIsValid;

// --- Output ---------------------------------------------------------
console.log('=== Task validation ===');
console.log(`id (${typeof id}):        ${id} → ${idIsValid ? 'OK' : 'ERROR'}`);
console.log(`title:              "${title}" → ${titleIsValid ? 'OK' : 'ERROR'}`);
console.log(`hours (${typeof estimatedHours}):     ${estimatedHours} → ${hoursAreValid ? 'OK' : 'ERROR'}`);
console.log(`priority:           ${priorityText} → ${priorityIsValid ? 'OK' : 'ERROR'}`);
console.log(`due date:           ${dueDateText} → ${dateIsValid ? 'OK' : 'ERROR'}`);
console.log(`assignee:           ${assignee ?? 'Unassigned'}`);
console.log(`tags (${tags.length}):           ${tags.join(' · ')}`);
console.log(`\nValid form: ${formIsValid ? 'YES' : 'NO'}`);

Output:

=== Task validation ===
id (number):        5 → OK
title:              "Set up the screen-printing workshop" → OK
hours (number):     6 → OK
priority:           medium → OK
due date:           2026-10-15 → OK
assignee:           Unassigned
tags (2):           workshop · training

Valid form: YES

Important decisions in this code:

  • Everything is converted explicitly. Number(idText) and Number(hoursText) instead of trusting + to behave.
  • .trim() before validating text. A title made only of spaces would pass a naive check.
  • The double check on the hours. hoursText.trim() !== '' is essential because Number('') returns 0, not NaN. Without it, an empty field would pass as "0 hours".
  • === in every comparison. The priority is compared against the three exact values.
  • The date is compared as text, taking advantage of the ISO format. '2026-10-15' >= '2026-08-18' is true.
  • A missing assignee is null, not ''. We are stating "there is no assignee", not storing an empty string.
  • Missing tags are [], not null. An empty list is still a list, and that way all the code downstream can treat it the same.

Validation from the interface's point of view —reading the real form, showing error messages next to each field, blocking submission— is the subject of Handling and Validating Forms. Here we have focused on the conversion and comparison logic, which is the part that really decides whether a piece of data is correct.

Common Mistakes and Tips

Common mistakes

  • Using == out of habit. It produces bugs that surface months later with specific data. Use ===.
  • Forgetting that Number('') is 0. An empty field passes as zero hours and nobody notices.
  • Relying on if (value) for numbers. If value is 0, the condition fails even though the data is legitimate.
  • Checking whether an array is empty with if (!list). An empty array is truthy. Use list.length === 0.
  • Comparing NaN with ===. It never works. Use Number.isNaN().
  • Using the global isNaN() function. It converts before checking and gives false positives. Use Number.isNaN().
  • Comparing objects or arrays with === expecting a content comparison. It compares references. Compare their fields instead.
  • Calling .toString() on something that might be null. Guaranteed TypeError. Use String().
  • Comparing numbers stored as text with < or >. '10' < '9' is true. Convert first.
  • Adding form values directly. '12' + '8' is '128'.

Tips

  • Convert at the boundary. The moment a value enters your program (a form, a URL, storage), convert it to the right type. From then on, all your code can trust it.
  • Memorize the eight falsy values. It is the table that will pay off the most out of this whole module.
  • When something behaves strangely, print typeof. console.log(typeof x, x) clears up half of all mysteries in two seconds.
  • Give your validations names: const hoursAreValid = .... An if with five unnamed conditions is unreadable.
  • Use ISO dates 'yyyy-mm-dd' and you will be able to compare and sort them as text without any library.
  • Number() to validate, parseInt/parseFloat to extract.

Exercises

Exercise 1: Predict the conversions

Write down the result of each line:

console.log(Number('  25  '));      // A
console.log(Number(''));            // B
console.log(Number('25 hours'));    // C
console.log(parseInt('25 hours'));  // D
console.log(parseFloat('7.5h'));    // E
console.log(Number(null));          // F
console.log(Number(undefined));     // G
console.log(String(null));          // H
console.log(Boolean('0'));          // I
console.log(Boolean([]));           // J
console.log(Boolean(''));           // K
console.log(Boolean(-1));           // L

Exercise 2: == versus ===

Fill in the table with the result of each comparison using both operators, and explain the three cases you find most surprising:

Comparison == ===
'6' ? 6
'' ? 0
'' ? '0'
null ? undefined
null ? 0
false ? 0
NaN ? NaN
'high' ? 'high'

Exercise 3: A task hours validator

Write a script that validates the "estimated hours" field of a Nómada Tasks form. For each input value, it has to check that:

  • It is neither empty nor made only of spaces.
  • It is a valid number.
  • It is greater than 0.
  • It does not exceed 40 hours (a full week).

For each input, print the original value, the converted value and the result (OK or the reason for the error).

Try it with these inputs: '8', '0', '', ' ', 'eight', '7.5', '45', '8 hours'.

Solutions

Exercise 1

Line Result Explanation
A 25 Number() ignores the surrounding spaces
B 0 The empty string converts to 0, not to NaN
C NaN Number() requires the whole text to be numeric
D 25 parseInt reads from the start and stops at the space
E 7.5 parseFloat keeps the decimals and stops at the h
F 0 null converts numerically to 0
G NaN undefined has no numeric equivalent
H 'null' String() works with null (unlike .toString())
I true The text '0' is not the empty string, so it is truthy
J true An empty array is an object, and all objects are truthy
K false The empty string is on the falsy list
L true Only 0 is falsy; any other number is truthy

Exercise 2

Comparison == ===
'6' and 6 true false
'' and 0 true false
'' and '0' false false
null and undefined true false
null and 0 false false
false and 0 true false
NaN and NaN false false
'high' and 'high' true true

The three most surprising cases:

  1. '' == 0 is true but '' == '0' is false. When both operands are text, == converts nothing and compares them as they are. This proves that == is not transitive: '' is "equal" to 0 and '0' is "equal" to 0, but '' and '0' are not equal to each other.
  2. null == undefined is true but null == 0 is false. There is no conversion logic behind this: it is a special rule written explicitly into the specification. null is only loosely equal to undefined and to itself.
  3. NaN == NaN is false. The only value in the language that is not equal to itself, following the IEEE 754 floating-point standard. That is why Number.isNaN() exists.

Exercise 3

'use strict';

// --- Business constants ---------------------------------------------
const MIN_HOURS = 0;
const MAX_HOURS = 40;

// --- Inputs to try ---------------------------------------------------
const input = '8'; // change it to try the others

// --- Step-by-step validation -----------------------------------------
const trimmed = input.trim();
const hours = Number(trimmed);

// A chained ternary: the order of the checks is deliberate
const result =
  trimmed === ''        ? 'ERROR: the field is empty' :
  Number.isNaN(hours)   ? 'ERROR: not a valid number' :
  hours <= MIN_HOURS    ? 'ERROR: it must be greater than 0' :
  hours > MAX_HOURS     ? `ERROR: it cannot exceed ${MAX_HOURS} hours` :
                          'OK';

console.log(`"${input}" → ${hours} (${typeof hours}) → ${result}`);

The expected results for every input:

Input trim() Number() Result
'8' '8' 8 OK
'0' '0' 0 ERROR: it must be greater than 0
'' '' 0 ERROR: the field is empty
' ' '' 0 ERROR: the field is empty
'eight' 'eight' NaN ERROR: not a valid number
'7.5' '7.5' 7.5 OK
'45' '45' 45 ERROR: it cannot exceed 40 hours
'8 hours' '8 hours' NaN ERROR: not a valid number

Three key observations:

  • The order of the checks matters. The empty-string check comes first, because Number('') gives 0 and that input would otherwise produce the message "it must be greater than 0", which is confusing and does not describe the real problem.
  • ' ' and '' produce the same error thanks to the .trim() applied beforehand.
  • '8 hours' is rejected because we used Number(). Had we used parseInt, it would have silently passed as 8. In input validation that would be a bug: Number() is the right choice for validating.

Conclusion

You have cleared the ground where people trip up most in JavaScript. You know how to convert explicitly with Number(), parseInt(), parseFloat(), String(), .toString() and Boolean(), and you know each one's traps (Number('') is 0, .toString() fails on null). You have a command of implicit conversion: + concatenates if there is a string involved, the other arithmetic operators convert to numbers. You know the complete list of the eight falsy values and you remember that '0' and [] are truthy. You can tell == from === and you have seen why loose equality is not even transitive. You know that strings are compared alphabetically —and why that makes ISO dates so convenient—, that objects are compared by reference, and that Object.is exists for the edge cases of NaN and -0.

And the most practical part of all: you have written a complete validator for the data of a Nómada Tasks task form, the kind that always arrives as text.

With this you close the basic toolbox: environment, syntax, variables, types, operators and comparisons. You now have everything you need to understand the project as a whole. In the last lesson of the module, The Course Project: Nómada Tasks, you will get to know Taller Nómada and its problem in depth, the canonical data model of a task with all its fields and permitted values, and the complete map of what you will build in each module of the course.

JavaScript Course: From Beginner to Advanced

Module 1: Introduction to JavaScript

Module 2: Control Structures

Module 3: Functions

Module 4: Objects and Arrays

Module 5: Advanced Objects and Functions

Module 6: The Document Object Model (DOM)

Module 7: Browser APIs and Advanced Topics

Module 8: Testing and Debugging

Module 9: Performance and Optimization

Module 10: JavaScript Frameworks and Libraries

Module 11: Final Project

© Copyright 2026. All rights reserved