JavaScript Error types
I know, the rest of this lesson looks rather boring, but if you've made it this far, please stick around because understanding error types, NaN, null, and undefined values is super important!
A JavaScript error happens when you try to execute JavaScript code that is either invalid or is incapable of handling the values you have given to it.
In JavaScript, there are several different types of errors, but they all "inherit" (this is an object oriented programming term) from the Error
object, which you can see the documentation for here.
While there are more than just three types, these three are the most common ones that you will see and need a high-level understanding of.
ReferenceError
SyntaxError
TypeError
JavaScript ReferenceError
When you try to "reference", or "use" a value that doesn't exist, you'll get this error. Here's the simplest example:
const myVariable = 20;
console.log(anotherVariable); // ReferenceError
JavaScript tries to find a "reference" to anotherVariable
in memory, but since we never declared it, it simply doesn't exist!
JavaScript SyntaxError
When we talk about "syntax", we are talking about how we write our code. If you write invalid JavaScript code, the compiler won't know what to do and will throw a SyntaxError
. This one is pretty easy to explain–just write some invalid JavaScript code! See if you can figure out what is wrong below.
const myObj = {
prop1: 'some value';
prop2: 'another value';
};
If you try to run this code, you're going to get a SyntaxError
that says Unexpected token ';'
. That is because instead of ;
, you need ,
in your objects. Here is the correct way:
const myObj = {
prop1: "some value",
prop2: "another value",
};
JavaScript TypeError
This is probably the hardest of the three to understand. It occurs when you try to perform an operation that cannot be done on a specific type of data. If you try to pass an incompatible argument into a function, attempt to modify an immutable value, or just use a value inappropriately, you will get this error.
It is confusing because there are many cases that seem like they would throw a TypeError
, but don't. Consider this:
const myObj1 = { prop1: 20 };
const myObj2 = { prop1: 50 };
// Does not throw an error
const result = myObj1 + myObj2; // "[object Object][object Object]"
You can't add two objects right?? No, you can't, but it won't throw an error at you if you try. It will just combine the two objects together in a string. Logically speaking, this seems like a TypeError
to me. But here are a few examples that actually do throw this error.
const myNumber = 50;
const myObject = {
prop1: "some value",
};
myNumber.toUpperCase(); // TypeError: num.toUpperCase is not a function
myObject.prop1(); // TypeError: myObject.prop1 is not a function
In the first case, we are trying to use a String method on a number. In the second case, we are trying to invoke a function when we are really dealing with a String.
What is "Error Handling"
The last thing that I want to cover with errors is something very important, but lost on a lot of beginners (including myself years ago).
What is the point of "handling" errors, and what does that even mean?
Well let me paint a picture for you. Let's say that you built an application similar to Instagram and one of your users loses internet connectivity while posting a picture. Clearly, the code that allows the user to post that picture is not going to work because the user doesn't have internet access.
If we handle the error in our code, we can print something on the screen that says, "You are not connected to the internet. Please connect and try again".
If we DO NOT handle the error in our code, our app is going to crash and the user is going to have no idea what happened.
So the next question is... What errors are we trying to handle?
And this is where it is difficult for beginners to understand error handling. In most cases, the errors that we want to handle are ones caused by external code that we have no control over. We will cover this in depth when we get there later in the series, but for now, I'll just show you how to handle errors.
try {
const num = 20;
num.toUpperCase();
} catch (err) {
// If the code in the try {} block throws an error,
// we will reach this code block and `err` will represent the Error object
}
Of course the code above is useless and we would never write something like this, but it demonstrates the try/catch syntax that we can use for error handling in JavaScript.
Since we put num.toUpperCase()
(which throws a TypeError
) in the "try" block, our code runs just fine without being interrupted. We could even print some details about this error.
try {
const num = 20;
num.toUpperCase();
} catch (err) {
console.log(err instanceof TypeError); // true
console.log(err.message); // num.toUpperCase is not a function
}
As I mentioned, we will be revisiting error handling throughout this series, so consider this your brief introduction.
NaN, null, undefined in JavaScript
I'm going to keep this final section short and sweet. There are three "data types" that we have not spent much time on, and those are NaN
, null
, and undefined
.
NaN - "Not a Number"
You will rarely see this or use this, but you should know what it is.
From the documentation, here are the most common scenarios that will return NaN
.
const myString = "some string";
// 1. Trying to coerce a string to a number
Number(myString); // NaN
// 2. Performing an impossible math operation
Math.sqrt(-1); // NaN
// 3. Operand of an argument is NaN
Number(myString) + 20;
// 4. Trying to use an arithmetic operator (other than + ) on a string
myString * 2;
Like I said, you won't see or use this much.
null
Unlike NaN, you'll encounter null
values all the time! A null
value is a JavaScript primitive value (remember from earlier in this post?) and represents the intentional absence of a value. In other words, you can think of it like a "placeholder" value that must be set by the developer.
When using null
in an operation, it behaves as a "falsey" value. See below.
let myVariable = null;
if (myVariable) {
console.log("this line will not print");
} else {
console.log("this line will print");
}
Here is the official documentation for null
values.
undefined
Very similar to null
, undefined
is a primitive value that represents the absence of a value.
You will get an undefined
value when you try to use a variable that exists, but is not defined yet (and has not been assigned a null
value).
let myString;
const myObj = {};
console.log(myString); // undefined
console.log(myObj.someFunction); // undefined
myObj.someFunction(); // TypeError
Since we didn't intentionally initialize myString
as a null
value, it carries an undefined
value.
The myObj
example is a bit trickier. You might infer that because myObj
does not yet have a someFunction
property, it would throw an error. Instead, all object properties that have not been assigned carry a value of undefined
. In the example, when we try to invoke this function, we get a TypeError
because you cannot "invoke" and undefined
value.
Like null
, the undefined
primitive is treated as a "falsey" value when used in a conditional.
let myVar;
if (myVar) {
console.log("this line will not print");
} else {
console.log("this line will print");
}
10 JavaScript Challenges
I have chosen 15 challenges for this lesson that will require you to apply the basics of the topics we covered here combined with the knowledge you acquired through prior lessons.
To get the most out of these challenges, I recommend watching my YouTube video where I solve all of them with you. I walk you through my thought process and hopefully fill in some gaps from these lessons.
Here are the challenges and solutions.
- Challenge Collection - Lesson 6 Collection on Codewars