- One of the hardest parts about writing TypeScript is
actually debugging the errors that TypeScript throws you.
So in this video, I'm gonna give you a few different hints
and tips for debugging TypeScript errors,
as well as how you can navigate through third party code,
which makes it easier to figure out
why these errors are being thrown.
So to get started, we're just gonna be talking
about how can you debug errors in the simplest way possible.
So we have a really simple function here
called print number, it takes in a number and some options.
I define these options up here,
a debug property that's a Boolean,
as well as a format object that takes tabs
which is a Boolean, and a tabWidth which is a number.
Right now, we have no errors,
and everything's working as expected.
Now, if I change this number to a string,
you can see that I'm getting an error,
and this error is relatively straightforward to understand.
It just says that the argument of type string is
not assignable to the parameter of type number.
And generally, if you have like, a one level deep error,
it's really easy to understand,
'cause it shows you the error right there.
But as soon as you start getting into objects
or types that are nested inside of each other,
for example, this options object,
has all these different types inside of it,
so we have a bunch of different nesting going on,
so when we have that happen,
especially with really complex objects,
our error messages become quite a bit more complicated.
I'll show you what I'm talking about.
Let's just change this back to a number,
and now let's try to pass in just an empty object.
Obviously, that's gonna throw an error,
'cause it expects all these different properties,
and we hover over this, you're noticing now
that the error is a little bit harder to understand.
It's not terrible, but it's still a little bit harder.
Essentially, it's telling me here that the argument type
of empty object is not assignable to our options type,
and that's because this empty object type is missing
the following properties.
That mostly reads like English,
and isn't too hard to understand.
But if we take this a step further
and we start adding some things inside of here,
for example, we know it has a debug property,
we'll just set that to false,
and we have a format inside of here,
and for this format we have some tab stuff,
so we'll say tabs is going to be true,
and let's say, you know what?
For tabWidth I accidentally put false,
I put a Boolean instead of a number.
Now when I hover over this, I'm getting another error,
type Boolean is not assignable to type number.
Okay, that's really good, that's exactly what I expected.
What happens if we leave this off, what do we get now?
When we hover over this,
you can see we're getting an error here.
And again these errors aren't too terribly bad
to understand, but again, as things get more complicated,
these errors will get much larger and much harder to read.
To show you a bit of an example of what I'm talking about,
I'm actually gonna take some code from a different project.
I'm just gonna pull it over for you.
As you can see here, this is the setup session function,
and it just has this giant options object.
So options object contains a bunch of different information,
and somewhere in there I've made a mistake.
As you can see, I'm getting an error down here,
and when I hover over this error,
you can see my error message is quite a lot larger,
and it's frankly quite difficult
to really understand what's going on,
and this is where my first tip
for TypeScript errors is going to be.
Generally, the actual error
that you're getting is going to be in the last sentence
or the very last section of your TypeScript error.
So really what you wanna do is you wanna scroll down
to the very bottom, and that's where
you're most likely going to find your error.
So if I read down here, it says type someValue
anotherNestedValue number is not assignable
to type Boolean or undefined.
Okay, and if I go up a step further,
it says the type cookie.httpOnly is not compatible
between these two types.
That right there gives me the error that I'm looking for.
If I scroll up here,
you can see this cookie httpOnly property is not the same
between the two different types,
and if I look back at this error, it's telling me the reason
for that is because the type I supplied is not
either a Boolean or undefined.
If I were to come up here and I change this
to a Boolean or undefined, for example,
I just changed this real quickly to like true,
now if I scroll down, you can see it got rid of the error.
So generally, when you're dealing
with those really long complicated error blocks,
the best thing for you to do is to just come in here
and look at the very last sentence,
because that a lot of times will tell you what the error is,
or maybe the last couple sentences.
Most of this stuff up here is kind of getting you
into that nested layer.
It's essentially going from the top level,
all the way down to the lowest level.
So at first, it's saying this giant options object is
not assignable, and then it's saying this particular part
of the options object is not assignable, and then finally,
the reason it's not assignable is because of this.
So the more nesting you have
inside of your different objects and your options,
it's going to become more and more of an error message,
and again, the most important part is pretty much
just going to be at the very bottom for you.
Now, another way that you can really understand
TypeScript errors a little bit better is
if you're using VS Code, there's an extension
that makes these error messages a lot easier to read.
If we go to the extensions tab,
we can search for TypeScript Pretty,
and I believe this is called Pretty TypeScript Errors.
Here we go, Pretty TypeScript Errors.
I'm just going to enable this right now.
And if we come back over to our code,
and now we look at this error,
you're gonna notice we still get the same original message,
but if we scroll down a little bit further,
we get this section that is nicely formatted,
and as you can see, it's saying argument of type,
and it's giving me my full thing, is not assignable
to SessionOptions, and then it's telling me the types
of cookie.httpOnly are incompatible, and then finally,
that the thing I passed in is not assignable
to type Boolean or undefined.
It's the exact same message as up here,
but they just formatted it in a way
that's much easier to read and understand what's going on.
And I really like how they have the indentation to tell you
when you're going to different levels of nesting,
and overall, this is just much easier to parse and read,
because the most important parts are syntax highlighted,
and they're kind of like, outlined with this background,
which makes it really easy to see.
Now, sometimes even while looking at the error message,
and reading through all that it has,
and with the prettier error messages,
you may still not know 100% why you have a problem,
because maybe the type that it says
that you can assign right here isn't as simple
as Boolean or undefined.
It may be something more complicated by just saying
like, session options or something generic like that.
So that's where having the ability to actually dive in
to this third party code
or whatever code you're using, and seeing
what all the implementation details are really helps.
So no matter what code you're using,
whether you're using third party code like I am here,
or if you're using your own code or a built-in code,
the same thing applies for all of them.
What you can do is wherever you're trying to use your code
where you're getting error, for example, this function,
you can right click on this, and you can click
on the go to definition, which is an F12 hotkey,
or what I generally do on a Windows computer is
you can hold down control, or command on Mac,
and you can click on the function name.
And what that'll do is it'll bring you
to the definition for that.
It opens up in this weird little popup.
If you double click in here, it'll open it up full screen,
and now you can see exactly where that's coming from.
Now, in this project, it just brought us to the same file,
'cause I'm doing some weird overriding stuff.
For now, I'm just gonna comment this out,
and we're gonna do the exact same trick.
So we're gonna click on this,
and now you can see it brought me directly
to where that function is declared,
and it gives me all the different types for everything.
So you can see this is an index.d.tss file.
We'll talk later about what these .d files are.
But if you look at the actual types here,
you'll see we're in our node_modules folder,
'cause this is where we've installed this particular library
and now it's telling me
that this option is an optional object
that is of this specific type.
And if I wanna learn what this type is,
again you can do the same trick of control clicking on it,
and you can see it brought me down to this line right here,
which is this interface for these options,
and you can see it's describing what everything is.
There's huge documentation in here,
describing what every single one
of these different properties means,
and in our case, we had problems with this cookie.
So we can go to our cookie options next,
and we can look inside of here and you can see,
okay, we have this, we have this,
and the problem I was running into was HTTP only,
and you'll see, okay, here's the type, Boolean or undefined.
Mine was not following this Boolean or undefined,
so that's why I was running into those problems.
So you can use that control/command click kind
of technology to navigate further and further,
and if you need to,
there's a back button up here in VS Code,
so I can just keep going back, forward,
and back and forward to try to figure out
exactly what's going on inside of this third party code.
This is a really powerful tool
when you really need to go an extra step
into the debugging process
when the actual error messages aren't clear enough,
or maybe you're trying to use something
you don't know why it's not quite working right.
This is a great way to look at just the types of the code.
It doesn't give you any of the implementation or JavaScript.
This just shows you what the types of everything are,
which makes it easier to read since you don't have to worry
about implementation, you can just see the type information.
Now if we just go back to our simpler code here real quick,
there's a few other tricks that you can use
when it comes to errors inside your types groups code.
And this is especially useful when you're converting code
from JavaScript to TypeScript, or maybe you have some code
that doesn't quite work with TypeScript,
and that is the ability to ignore errors
that show up in your code.
Let's say, for example, that this code is actually correct,
and this error that I'm getting from TypeScript
on this format thing, it's just a bad error for some reason.
Well, if that was the case, I wouldn't be able
to compile my code with this error in there,
so instead what I need to do is I need to write a comment
that tells TypeScript, "Hey,
I expect there to be an error there.
Just ignore this line of code completely,
and do not do anything with it."
So if you write a comment like this, and you say @ts,
you'll notice we have a couple different options.
The one I'm gonna look at here is @ts-ignore.
What that does is it just tells the TypeScript compiler,
"Ignore the next line completely.
Just pretend it doesn't even exist
when you do your type checking."
Now you'll see that this error no longer shows up,
because it's not actually type checking
this line right here.
Now, this is obviously something you should use
incredibly sparingly, because by doing this,
you just turn off all the benefits of TypeScript,
and you essentially go back to JavaScript
for one line of code,
but in very specific niche scenarios,
where maybe you have a bug or something in like,
some library code you're using
where their types are wrong or something like like that,
this could come in handy to make sure
you can still compile your code
while getting all your TypeScript benefits,
and only ignoring it in specific cases where it's broken,
based on something that's out of your control.
Generally, when I have those types of situations, though,
instead of using ts-ignore,
which just says ignore the next line completely,
don't throw any errors ever,
I like to use the ts-expect-error.
This is slightly different
in that it'll actually expect there to be an error,
so if there is an error on the next line,
everything is perfectly fine,
but if you don't have an error on the next line,
like let's say I just actually fixed this code
by giving this a tabWidth,
you'll now see I get an error here saying
that error is expected to be an error but there is no error.
This is really great if you have something
that you think will get fixed in the future,
or that you plan on fixing in the future.
Putting an expect error in here means
that if it ever does get fixed,
they'll make sure to see that this is now no longer needed,
and it'll be able to get removed,
which means this line will now have TypeScript be applied
to it again instead of permanently being ignored.
Because generally, if you see a ts-ignore in your file,
you don't want to remove it,
'cause you don't know the reason for it being there,
so it just kind of sticks around forever.
That's kind of why I like expect-error instead.
Also, something that I think is a really good practice is
if you're using ignore or expect-error,
a good thing to do is just describe
what you're trying to ignore,
what error you're running into, so kind of like,
copy paste the TypeScript error, or just mention like,
we are ignoring this error because of X, Y, and Z issue.
That's a really great thing,
because now people know why this is there,
and they can know if they can remove it in the future
when those different things are fixed or changed,
or maybe there's a new way to do things,
where they can get around that particular thing
that you're mentioning right here.