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
- Why type conversion exists
- Explicit conversion to a number
- Explicit conversion to text
- Explicit conversion to a boolean: truthy and falsy
- Implicit conversion (coercion)
- Comparison operators
==versus===- Comparing strings and comparing numbers
- Comparing objects: the reference trap
Object.isand the edge cases- Practical hygiene rules
- Application: validating the data from a task form
- Common Mistakes and Tips
- Exercises
- Conclusion
- 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' * 2 → 24 |
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.
- 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)); // NaNTwo results deserve special attention because they cause real bugs:
Number('')is0, notNaN. If a user leaves the hours field empty, you will get0instead of an error warning.Number(null)is0butNumber(undefined)isNaN. 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 0Always 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:
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():
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); // trueBoth checks are needed: Number.isNaN rules out non-numeric text, and trim() !== '' rules out the empty string, which Number() would happily turn into 0.
- 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()); // TypeErrorString() 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:
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 |
- 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)); // false4.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 0The 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 checktags.length === 0, notif (!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
}
- 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 5The 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 ← correctAnd 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 correctlyThe 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.
- 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
== versus ===
== versus ===Here is the heart of the lesson.
===(strict equality) compares value and type. If the types differ, it simply returnsfalse.==(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 compares7.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 false → 0 |
[] == '' |
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:
7.3 The case of NaN
NaN is the only value in JavaScript that is not equal to itself, neither with == nor with ===:
That is why Number.isNaN() exists:
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 NaNAlways use Number.isNaN().
- 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:
To sort text according to a language's own rules (accents, special letters) there is localeCompare:
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 orderThe 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.
- 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 objectThe 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 worksflowchart 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.
Object.is and the edge cases
Object.is and the edge casesObject.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.
- Practical hygiene rules
Five rules that will spare you most of the problems in this lesson:
- 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. - Convert explicitly before operating. If a value might be text, apply
Number()to it before adding it. Do not rely on coercion. - Do not mix types in the same variable. If
hoursis a number, let it always be a number. - Check for the absence of a value explicitly.
if (value === null)orvalue ?? fallback, instead of relying on truthy/falsy. - 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
- 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)andNumber(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 becauseNumber('')returns0, notNaN. 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'istrue. - A missing assignee is
null, not''. We are stating "there is no assignee", not storing an empty string. - Missing tags are
[], notnull. 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('')is0. An empty field passes as zero hours and nobody notices. - Relying on
if (value)for numbers. Ifvalueis0, the condition fails even though the data is legitimate. - Checking whether an array is empty with
if (!list). An empty array is truthy. Uselist.length === 0. - Comparing
NaNwith===. It never works. UseNumber.isNaN(). - Using the global
isNaN()function. It converts before checking and gives false positives. UseNumber.isNaN(). - Comparing objects or arrays with
===expecting a content comparison. It compares references. Compare their fields instead. - Calling
.toString()on something that might benull. GuaranteedTypeError. UseString(). - Comparing numbers stored as text with
<or>.'10' < '9'istrue. 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 = .... Anifwith 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/parseFloatto 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)); // LExercise 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
40hours (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:
'' == 0istruebut'' == '0'isfalse. When both operands are text,==converts nothing and compares them as they are. This proves that==is not transitive:''is "equal" to0and'0'is "equal" to0, but''and'0'are not equal to each other.null == undefinedistruebutnull == 0isfalse. There is no conversion logic behind this: it is a special rule written explicitly into the specification.nullis only loosely equal toundefinedand to itself.NaN == NaNisfalse. The only value in the language that is not equal to itself, following the IEEE 754 floating-point standard. That is whyNumber.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('')gives0and 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 usedNumber(). Had we usedparseInt, it would have silently passed as8. 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
- What Is JavaScript?
- Setting Up Your Development Environment
- Your First JavaScript Program
- JavaScript Syntax and Basic Concepts
- Variables and Data Types
- Basic Operators
- Type Conversion and Comparisons
- The Course Project: Nómada Tasks
Module 2: Control Structures
- Conditional Statements
- Loops: for, while, do-while
- Switch Statements
- Flow Control: break, continue and Nested Loops
- Error Handling with try-catch
Module 3: Functions
- Defining and Calling Functions
- Function Expressions and Arrow Functions
- Parameters and Return Values
- Scope and Closures
- Hoisting and the Execution Context
- Higher-Order Functions
- Recursion
Module 4: Objects and Arrays
- Introduction to Objects
- Object Methods and the
thisKeyword - Arrays: Basics and Methods
- Iterating over Arrays
- Searching, Sorting and Aggregating Data: find, sort and reduce
- Array Destructuring
- Object Destructuring, Spread and Rest
- JSON and Copying Objects
Module 5: Advanced Objects and Functions
- Prototypes and Inheritance
- Classes and Object-Oriented Programming
- Encapsulation: Getters, Setters and Private Fields
- Modules: Import and Export
- Asynchronous JavaScript: Callbacks
- Promises and Async/Await
- The Event Loop and the Microtask Queue
- Iterators and Generators
Module 6: The Document Object Model (DOM)
- Introduction to the DOM
- Selecting and Manipulating DOM Elements
- Handling Events
- Propagation, Delegation and Custom Events
- Creating and Removing DOM Elements
- Rendering Lists and HTML Templates
- Handling and Validating Forms
Module 7: Browser APIs and Advanced Topics
- Local and Session Storage
- The Fetch API and AJAX
- Robust Requests: Errors, Timeouts and AbortController
- WebSockets
- Service Workers and Progressive Web Apps (PWAs)
- Essential Browser APIs
- Introduction to WebAssembly
Module 8: Testing and Debugging
- Debugging JavaScript
- Code Quality: ESLint, Prettier and Conventions
- Unit Testing with Jest
- Test Doubles: Mocks, Stubs and Spies
- Integration Testing
- End-to-End Testing with Cypress
Module 9: Performance and Optimization
- Measure Before You Optimize: DevTools and Web Vitals
- Optimizing JavaScript Performance
- Memory Management
- Efficient DOM Manipulation
- Lazy Loading and Code Splitting
Module 10: JavaScript Frameworks and Libraries
- Why Frameworks Exist
- Introduction to React
- State Management with Redux
- Vue.js Basics
- Angular Basics
- Choosing the Right Framework
