Alright! Hello everybody! Today I wanted to go over the very basics of how types work in TypeScript so that you can start using them in the programs you write. If you don’t have TypeScript installed, please see my prior videos where I’ll show you how to do that.
I’ve started here with an empty folder in VSCode, feel free to use any text editor of your choice as you follow along. Before we get started, we’re going to open up a terminal and create a TypeScript configuration file by running “tsc –init”
Now let’s create our main.ts file where we’ll be doing everything today.
And then we’re going to go to Terminal and click Run Task and do “tsc –watch” to get TypeScript compiling our files.
The first thing I want to touch on before we go much further is that TypeScript is really good at figuring out what the types of things are supposed to be without you needing to explicitly declare it.
For example, you can see here in Visual Studio Code we can figure out what the type of something is by hovering our mouse over, and we see that it pops up what it knows about this variable. It says “bar: number” which means it’s already figured out that this variable is a number!
However, you’ll often want to explicitly tell TypeScript what the type of something is, so that’s what we’ll be focusing on in this video. Now, as it turns out, that same syntax VS Code showed is what you’ll use to explicitly tell TypeScript what the type of something is.
We can say here “let bar: number”, and TypeScript will start enforcing that this variable can only ever be a number.
You’ll see if we try to change the value to something else, we’ll get a red underline, an error listed below, that this string is not assignable to type number.
And, just to be clear, if we remove the “: number”, we still get the same error, because TypeScript is going to assume that once you assign a variable a value of one type, that it was probably a mistake to use any other type.
Let’s start with the what the most basic types are in TypeScript: booleans, numbers, and strings.
A boolean can be either true or false.
A number can be any number you like.
And a string can be any text that you like.
Building on top of the basic types, we can do arrays of things by specifying the type and then writing an opening and closing square bracket.
There is an alternate syntax for writing arrays, though generally I don’t see it used as often. Instead of using the square brackets, you write Array, an angle bracket, the type contained in the array, and then a closing angle bracket. This is completely the same as the other syntax used above, and like I said, this is used much less commonly so I say default to the first way unless you have a good reason to do otherwise.
There’s also a variation on arrays called a tuple. If you’re not familiar with the term, you can think of it simply as an array with a fixed number of elements.
Say there are two pieces of data you always want to store next to each other, but don’t want to use a full object for whatever reason, you could do that with a tuple. For example, we could store student test scores as a tuple containing their name and the score.
Here, Tom has a score of 97.5.
And and you can see this is expecting just a string and a number. If we tried to add something extra to this, it’s going to complain to us that we’ve created something that’s not the size that we said it would be or if we try to remove a required element TypeScript will also complain. And similarly if we use the wrong type it’ll complain as well.
To be clear, you can have an array of tuples by combining the same syntaxes we’ve just learned. So we’ll have our tuple type followed by the opening and closing square brackets.
And then we would fill it out like so, and TypeScript will enforce that all the types we use here are correct.
Declaring our own objects is done in a very similar way to arrays. Let’s say we’re making a game, and we have a player with an x position, a y position, and some health. How would we do this in TypeScript?
We’ll start by declaring our player variable, then doing a colon and some curly brackets, and inside the curly brackets we can put the names and types of the fields we want to create. You’ll see that this looks very much like the actual object value that we’ll assign to the player object.
If we accidentally put in the wrong type, or if we add a field that doesn’t exist as part of the definition, TypeScript will complain at us.
It’ll often be the case that you’ll want to re-use object definitions like this in multiple places, say if you had multiple players or various functions that manipulated player objects.
There are two ways of doing this, the first and most direct way is to use the “type” keyword to define a new name that is equal to the type we defined for players earlier.
Then we can use that name in place of the full object definition for the player.
Another option with very similar effects is to create an interface. You can do this by using the “interface” keyword followed by the name you want, and then describe the types of the fields for the object like we did earlier. We can use it as the type for our player in exactly the same way that we used the definition created by the “type” keyword.
When is it appropriate to use “type” instead of “interface”? The answer is a bit too long for this video, so stay tuned for a future one where I’ll cover that. For now, just use whichever one you feel most comfortable with.
Now, going back to our example, you can see that I can now create a second player using the same type.
And if I update the definition of the type, TypeScript will helpfully give us an error on both of them, indicating that they need to be updated to match the changes.
This feedback loop is super common in development. You’ll often notice some object needs a new field, or needs that field altered in some way. You can make that change in the central type definition, and then go through and fix each of the places where TypeScript indicates that there’s a problem. No need to worry about whether you updated every spot or not, TypeScript will let you know.
Now, you’ll see here that TypeScript is complaining about our HTMLElement variable. Why is it doing that? Well, if we hover over document.getElementById, we’ll see that the type is actually “HTMLElement | null”, the pipe character there means “or”. That’s because if we specify an ID that doesn’t exist on the page, this function will return null.
Using the pipe character to join multiple types together creates what’s called a “union type”, which is simply a fancy way of saying “this variable could be a few different things”. There’s a lot more to union types, and other related features in TypeScript that we’ll cover in a future video, for now, just be aware that if you have a variable that could be a couple different types, you can list those types by separating them with the pipe character.
Okay, so that covers the basics of builtin types and defining our own types. There’s a couple of catch-all types that I want to show you.
The first one is an “object”, and that represents anything that isn’t a basic type. So it’ll be anything that isn’t a boolean or a number or a string. For example, it could be HTMLElement or our Player type, or anything else. This is a type that I don’t see used all that often, because generally you either know what the object type is, or you use the even more general “any” keyword.
Next up is talking about how to handle null, undefined, and how to use types with function definitions, but this video is already running a little long, so stay tuned for part 2 of this video.
If you’re enjoying what you’re learning here, you might like the TypeScript course that I’m working on. Head on over to https://typescriptbyexample.com, and you can put your email address in the form at the bottom of the page to be notified when it’s ready.
Thank you so much for watching. I’ll see you in the next video.