Learn TypeScript #5, Basic Classes

Transcript:

Hello, everybody! Today I’m going to show you how to use classes in TypeScript. There’s a lot to cover about classes, so for this video we’re going to stick to the basics, and then in future videos we’ll dig into the really advanced stuff.

At their core, classes are a way of describing what data you want certain objects to hold, along with methods that act on that data in various ways. There are a lot of other features built on top of this, but that is the core.

If you come from a language like C# or Java, classes in TypeScript are very similar to what you worked with there. Though there are some important differences in the low-level details, which we’ll be covering in later videos.

Unlike Java or C#, however, there’s nothing in TypeScript that is forcing you to use classes. So if you prefer to write your code in some other way, you are perfectly free to do so. Classes are just another tool you have in your toolbelt as a TypeScript developer.

If you’ve been following the development of JavaScript over the years, you’ll know that the latest JavaScript standards have added support for classes. TypeScript classes build directly on these standards, but with the added benefit that you can use TypeScript to target older versions of JavaScript, and the classes you write will work perfectly well when compiled down to code that’s compatible with older browsers that don’t support classes in JavaScript directly.

So let’s start off with a simple example class.

Let’s say we’re making game and we’re planning on having multiple players and a decent amount of logic common to the players to handle things like movement, displaying the players, and so on. This sounds like a good candidate for a class, so let’s create a class called Player.

Creating a class is very simple, we’ll write the class keyword followed by our class name, and then the contents of the class are contained within curly brackets. If you saw the previous video on interfaces, you’ll notice that class definitions and interface definitions share a lot of parallels, which you’ll see more of going forward.

Let’s pause for a moment and note that this is actually a complete class. It’s empty, so it’s not very useful, but before we start putting things in it, I want to show you how to create an instance of this class.

We’ll create a new variable called “p”, and set it equal to “new Player()”.

The “new” keyword is how you indicate that you’d like a new instance of an object that’s defined by a certain class. Player is obviously the name of your class, and your parentheses indicate that this is a function call.

Now, we haven’t actually defined any function here, so what function is it calling? Well, all classes have what are called “constructor” functions, and if you don’t create one explicitly, TypeScript will create one for you. We’ll go into more detail on this in a bit.

Let’s log the variable and see what it looks like.

You’ll see that Node.js is actually very helpful here, showing us the class name, and then the empty curly brackets will contain the data in our object once we actually start adding some fields to our class.

So, how do we add things to our Player class?

The first and easiest way is to simply write the name of the variable, and then assign to it whatever value you want.

Here we’ll say each player needs and x and a y position, so we’ll set both of those equal to zero.

If we run our program again, you’ll see that I have a player with an x and y of 0. Excellent!

These fields are just like the fields of any other object. For example, I can set “p.x” equal to 1, and that will do what you expect.

A note to those from other programming languages: Fields in TypeScript are all publicly accessible by default.

Just to prove to you that we have an isolated instance of our object, let’s create a second variable called p2, and log that one out as well.

And you’ll see it’s only our original object that has changed, just as we would expect.

Going back to our class definition, note that if you wanted to be explicit about the type that you wanted a field to be, you can indicate that in the usual way by simply writing a colon and then the type that you’d like.

What we’ve done so far works well for fields that will always start off at some default value, but what about a case where there isn’t necessarily a correct default value?

For example, let’s say we wanted to give each Player a name. We’ll add the name field and say that it should be a string.

But, what’s a good default for that? Well, an empty string might not make much sense there. Neither would a random name. Plus then we’d have to remember to change it properly after instantiating it.

Probably what makes most sense would be for us to force the name to be specified at the creation of the object. We mentioned previously that there was a constructor function being created for us, let’s define it ourselves now so that we can use it to require a name.

You can do that by going to the class definition, typing “constructor”, parentheses and then curly brackets for the constructor function body.

We’ll add “name” as a parameter to this function, and we’ll assign “this.name” equal to the “name” parameter.

You’ll see that now TypeScript is giving us errors about the places where we’re creating instances of this class because we’re not providing a name.

Let’s name the first one “Tyler” and the second one “Fred”. And now TypeScript is happy again.

A couple points of clarification on constructor functions. First, the special “this” keyword refers to the particular instance of the class that you’re currently working with. Here in the constructor, that’s the newly created object that we’re building up.

Second, the way these default values for x and y are set by TypeScript is by moving the assignment to the very top of your constructor function.

So assigning a value to them on the same line as their definition is directly equivalent to writing “this.x = 0” at the top of the constructor function.

In general, since assigning the default value where the field is declared is more compact than the alternative, I tend to favor doing that in my own code. If you prefer to leave the declaration as only the types, and initialize all the defaults in the constructor that’s a perfectly valid stylistic choice.

Note that if you’re initializing something in the constructor, the field declaration must have a type on it, because otherwise TypeScript will not be able to infer what type it is that you want.

Okay, so we’ve got some data that we’re holding in our objects, let’s create a function to act on that data.

Let’s say we don’t think the output of console.log is quite to our liking, so we’d like some other way of displaying our Players.

Well, we can create a new method by specifying the name, the arguments in parentheses, and then we can specify the return type with a colon after the parentheses.

And we’ll type out a call to console.log() that formats things a little more nicely.

And we’ll run that, and hey! Things are looking pretty good.

One thing you’ll notice down below is that the list of functions that are part of an object aren’t included in the default console.log() output in Node.js. Generally this is fine, since the data will be more important to see, but this is something you should be aware of.

Let’s tie things into the previous video by showing how we can make interfaces and classes work together.

Since we’re working on a game here, I imagine we’ll have a number of different types of objects that we’ll want to treat generically in some kind of way. Perhaps we’ll use some common fields to do collision detection or to draw them or whatever else.

To define what we need, we can create an interface listing what we’ll need from each game object.

Let’s say this is a 3D game, so we’ll need an x y and z position.

In order to indicate that we want our player to be compatible with this interface, we can use the “implements” keyword followed by the name of the interface. Note that if you had multiple interfaces, you can separate them by commas.

Now we’re getting an error about our player not implementing the interface correctly because it doesn’t have the “z” field, so let’s add that.

And now everything is good.

Down lower in the code, we can create an array of GameObjects, and put our two player objects in that array. Since our Player class properly implements the GameObject interface, TypeScript is perfectly happy with this.

Note that the “implements” keyword on our class definition isn’t actually required as you might be used to from other languages. If we remove it, everything is still fine. The difference here is that if we were to remove our “z” field, we now get an error down in our array rather than on the class itself.

So why would you want to use the implements keyword if you can get away without it?

The biggest reason is that it will tell you while you’re focused on just the class definition itself whether or not the class properly implements the interface.

It also serves as great documentation for your future self and other programmers that the specific class is intentionally conforming to some interface.

A third reason, is that some frameworks like Angular will check objects for specific methods. If they exist, Angular will call them, if they don’t, Angular won’t. Including the “implements” keyword and the relevant interface for that specific Angular feature will cause TypeScript to validate that you are actually including that method properly. If not, you’ll get an error, which is helpful.

Okay, we’ll pause here and in future videos we’ll be covering things like inheritance, private, public, and protected fields, static properties, accessors, the nitty gritty details on the implementation details behind classes, and much more.

If you’ve enjoyed this video and want to go deeper, I’m working on a course on TypeScript. You can find it at https://typescriptbyexample.com I’m still working on it but if you scroll down to the bottom of the page leave your email address there I’ll shoot you an email when that course is released.

That’s it for this video! Thank you so much for watching, I will see you in the next one.

Learn TypeScript #4, Interfaces

Transcript:

Hello, everybody! In today’s video I wanted to show you interfaces, what they are, how to use them, etc. So let’s get started!

What exactly are interfaces? Well, interfaces are a way of ensuring that the objects you’re passing around throughout your program conform to your expectations. You can describe the shape of a paricular object, and the types of all its fields, and TypeScript will enforce that your expectations for that object match reality.

This becomes immensely valuable once your program starts to grow past even a few dozen lines long.

Interfaces are one of the basic building blocks of TypeScript code, and you’ll see them used everywhere. Here I’ve opened a typings file that’s internal to TypeScript itself, and if we search for interfaces we get 86 results just in this file alone! So they’re definitely something you want to be familiar with.

Interfaces pair really well with classes in TypeScript, and we’ll be going into detail on classes in a future video.

Let’s start with some simple examples of an interface. Say we’re writing a web app; we’ll likely have lots of different code that will need to work with user data, so user data seems like a good candidate for an interface.

We’ll create an interface using the interface keyword, followed by the name we want for it. I’ll call this User.

Then we’ll define the fields a user should have. Let’s say we want the name as a string, the time of their first visit as a Date, and their role is going to be either a “admin”, a “manager”, or a “employee”.

That’s a complete interface!

Let’s create a variable that uses this new interface. Like any other variable, we’ll give it name write a colon and then the type, which in this case will be the name of our interface.

We’ll then put in a name, create a new Date object which will hold the current time, and we’ll say the role is “admin”.

So what do interfaces look like when they’re compiled? Well, TypeScript interfaces are “zero cost”, which means when you compile your code they completely go away.

If we open up our compiled code, you’ll see that there’s no trace of our interface to be found.

This means that all the enforcement of the interfaces happens at compile time, and when your program is running, it’s not going to be slowed down by interfaces in any way.

On the other hand, if you’re receiving data from some external source that you’ve defined an interface for, TypeScript won’t be able to tell you if that external source has given you valid data.

So make sure that you write your own validation code in that case to prevent any runtime errors.

If you’ve been following along in the series so far, you’ll have seen me use the “type” keyword to create some new types that were useful for our programs.

The type keyword can describe objects in a similar way that interfaces can, but you generally won’t find it used that way.

Interfaces are the standard way of saying “here’s how I expect this object to look”, whereas the uses of the “type” keyword are more about combining various types in various ways.

You’ll see this more in the video on classes, where we’ll have classes declare that they “implement” certain interfaces, and TypeScript will ensure that they actually do.

In general, if you need to create a type for some specific object, use an interface. Otherwise use the type keyword.

One interesting thing you can do is create interfaces that are based off of other interfaces.

Let’s say we actually want to store some additional data on admin users. What we can do is say “interface AdminUser extends User”.

Currently I haven’t yet listed any fields. But if I were to change my use of type User to type AdminUser, TypeScript will continue to be happy because all the types from User have carried down to AdminUser.

For AdminUsers, let’s add an array of things that they have access to.

You’ll see that I now have this red underline here because the access property is missing. If we give this user access to a few things, TypeScript will be happy again.

An interesting thing interfaces can do is override fields that are defined in interfaces that they’ve extended.

A good example of this would be the “role” field. On AdminUsers, it’s always going to be “admin”, so let’s specify that.

Now if I try to change myself to an employee from an admin, you’ll see that TypeScript is now throwing an error.

Overriding fields like this is pretty useful, and you may see it in TypeScript code you encounter in the wild every now and again.

Another thing interfaces can specify is functions that should be a part of them. Let’s say for our AdminUsers we want some way of logging their name to the console.

You’ll simply write the function name, parentheses, the argument list and then the expected return value of the function.

Since it’s just an interface, we don’t provide any implementation for this function, that’s left up to the actual instances of the object.

We’ll add a function down here to satisfy the interface, and TypeScript is happy with us again.

Note that if we do something like alter the expected return type, TypeScript will helpfully indicate the error so that we know to update the usages of that interface.

Another very helpful thing you can do with interfaces is define optional properties. So let’s define an optional property called “jobTitle”. All that it takes to do that is to add a question mark at the end of the name. And you’ll see that since it’s optional, TypeScript isn’t giving us an error about the jobTitle field being missing from our user object.

Of course, it will give us an error if we define the field with the wrong type.

If you have strictNullChecks turned on, an optional field can only be the type you specify or undefined. It can’t be null. If you need null as a valid value, you’ll need to explicitly spell that out in the type definition.

If you want to prevent people from modifying an object that implements a certain interface, you can describe certain properties as “read only” by prefixing them with the “readonly” keyword.

Let’s create a variant of our User interface that is all readonly called ReadonlyUser. Perhaps we want to make sure that our user interface code is only displaying users, rather than modifying them in any way.

So now if we create a new ReadonlyUser, TypeScript will give us errors if we try to modify any of the fields.

Note that this is a fairly weak guarantee, however. If we create a variable of type User and assign our ReadonlyUser to it, TypeScript will be perfectly happy doing so, because all the necessary fields are present. But now we can modify the ReadonlyUser by way of this plain User variable.

So think of readonly as a light helper or as a bit of documentation to other programmers, rather than a strict guarantee that the field marked as read only will never change.

Moving on: a common mistake that you might make while using an interface is making a typo on one of the field names. TypeScript has a feature called “excess property checks” which aims to help prevent this kind of mistake.

Looking at our AdminUser here, let’s typo the “name” field.

TypeScript is now giving us an error that this unknown field does not exist on type AdminUser, which helpfully let’s us catch our typo.

However, the excess property checks only happen on object literals. So, that’s situations like this where we’re assigning directly to a variable, or if you were to pass an object literal to a function that’s expecting an interface, TypeScript would do these checks as well.

However, if you’re pulling in a variable in some other way, the excess property checks aren’t going to happen.

To show you what I mean, let’s change our variable declaration a little bit, and add a typo on the jobTitle field.

We’ll then create a new variable of type AdminUser and assign our previously created variable to it.

Since excess property checks won’t be used in this situation, TypeScript is only guaranteeing that the minimum required fields are present, which they are. The field with the typo is just ignored as extra data which isn’t important to the interface.

This problem very well illustrates a general principle of how TypeScript interfaces work: TypeScript will guarantee that the object has at least the fields you describe. They might have more fields, they might have extra functions, they might have none of their optional properties, they might have whatever else. But you can be sure that they have at least what you specified.

That’s especially useful to know if you’re going to do something like loop over all the fields in an object. If you do that, TypeScript is only guaranteeing that you’ll see the minimum required fields, but you might see more things depending on where your data came from, so you’ll need to make sure your code will handle that properly.

Generally you won’t run into these kinds of issues on a day-to-day basis, but it’s really helpful to be aware of them for when you eventually do.

Moving on, let’s say you have an object that you’re using to store other objects. For example, we might want to have a mapping between a user’s ID and the user data itself. You can use what are called “index properties” to describe this sort of situation.

Let’s create a MapOfUsers, and we’ll associate a string ID to the user data.

You’ll see here in square brackets we define the type we’ll be accessing with, and then after a colon outside the square brackets, we list the type we’ll return with that access.

Let’s use this new MapOfUsers type. We can put in our existing user, and that’ll work just fine.

We could also define a new user in-line, and TypeScript will enforce that it has the proper values.

TypeScript will also enforce that you use the proper index type. Here we used strings, but we could also switch to numbers instead.

You’ll see that after making that change, TypeScript indicated an error until we switched all of our values from strings to numbers.

The final feature of interfaces I wanted to show you is the ability to modify previously declared interfaces. I don’t see this used very often, but it’s good to know that it’s something that exists.

In order to do this, you simply write out the interface keyword and the name of the interface you’re going to modify. Let’s let’s pretend that this interface user is defined in some file A and this other one is defined in some file B. Perhaps in file B we’re asking the user for their age and we want to indicate that as part of the user document.

You’ll see now down here on the user we’ve created, we’re now getting an error about age not existing. So as simple as that we added a new field to the interface. Perhaps we should mark it as optional.

You’ll want to be really careful when you do this, because adding new required properties could easily break your existing code. Your code will also become harder to follow because now your interface definition is split across multiple places, so it’s hard to get the big picture view of what the whole thing looks like.

The main use case for this feature is if you are modifying a library that you’re using, or if you’re monkey patching one of the built in JavaScript objects. Otherwise I reccomend against you using this feature.

Alright! That’s it for interfaces! Stay tuned for the upcoming video on classes, which interact with interfaces quite a bit.

If you’ve enjoyed this video and want to go deeper, I’m working on course about TypeScript. You can find it at https://typescriptbyexample.com Scroll down to the bottom and put in your email address, and I’ll let you know when the course is released.

Thank you so much for watching! I’ll see you in the next video.

Learn TypeScript #2, The Basics of How Types Work Pt. 2

Transcript:

Alright! Hello everybody! This video is a continuation of the previous video on basic types in TypeScript. Today we’re going to go over null, undefined, using types with functions, and a few other things. If you missed the previous video, you might want to go back and watch it before you start this one.

There was one thing I wanted to clarify from the last video. I mentioned that I strongly recommended against using the “any” type unless you absolutely had to, and I wanted to explain why. The basic reason, is that when you use the “any” type, you’re opting out of nearly all the checks that TypeScript is going to do for you, so if you’re going to use “any” everywhere, you might as well not use TypeScript.

I know when I first started with TypeScript, I often found myself reaching for the “any” type, but 99 times out of 100 it wasn’t because I needed it, but because I was being too lazy to stop and think about what the type of something should be.

If you take those few seconds to stop and think, you’ll end up with a program that’s better designed, you’ll have code that’s easier to come back to and change later, and you’ll have TypeScript helping you catch errors later on down the road.

Enough about “any”, let’s talk about null and undefined. This is definitely one of the more confusing parts of JavaScript (and therefore TypeScript), so let’s talk a little bit about what null and undefined are.

You may be familiar with null from other programming languages, and the easiest way I find to think about null is that it represents the explicit lack of any value. To use our player object example, if there’s no player 2, it’s possible that we’d want our player2 variable to be null.

Undefined is very similar, as it also represents the lack of any value. The key difference here, is that any variable you never assign any value to is going to hold undefined by default (though you could also explicitly set a variable to undefined, like you can set one to null).

Generally, I see null as representing a value that is intentionally missing for whatever reason. Whereas when undefined crops up in a program, it’s often a mistake or accident rather than intentional, since it implys that somebody created a variable, but then never used it. Due to this, I recommend that if you need to indicate a lack of some value, use null rather than undefined, since it will be clearer to people interacting with your code that this was probably something intentional.

All that aside, how do we use null and undefined in our TypeScript programs?

You’ll remember from our last video that we could use “null” as a type just like string or boolean or anything else, and the same is true for undefined.

Notice that if we create a variable and don’t assign to it before we try to use it, TypeScript helpfully gives us an error.

Note also that even though variables default to undefined, we can’t actually assign the value undefined or null without specifying it as part of the type of our variable.

You’ll see we get the error “type undefined is not assignable to type string”.

We have to explicitly list it as a possible type in order for TypeScript to accept it.

You’ll see also that if we try to use this variable without checking wether the type is actually not null or undefined, TypeScript will give us an error as well, preventing us from making a common mistake where we assume a variable has been assigned some value, when really it could just as easily be null or undefined.

Now, it’s important to note that TypeScript actually has a mode where it will be less strict about the usage of null and undefined. If we open up tsconfig.json and scroll down a little bit, you’ll see that I have strict mode enabled. This enables all of the strict checks listed below it, but the one that is most relevant to us is “strictNullChecks”.

If we disable strictNullChecks, or if we disable all of the stricter checks, then TypeScript will become more lenient on us and allow us to set any variable to null or undefined.

Generally I recommend against taking the more lenient route, because it leads to a lot of potential errors where you assume something will have a proper value when it actually doesn’t. It’s much better to explicitly document using the type system that a value might be null or undefined so that the proper checks can be made.

The main case for disabling strictNullChecks is if you’re converting a large existing JavaScript project over to TypeScript and you don’t want to have to deal with all the errors TypeScript starts throwing about not checking for null or undefined, since it’s common for JavaScript developers to be more wonton about their use of null and undefined. You can still get a lot of benefits out of TypeScript without enabling strictNullChecks, but again, I recommend that you enable it.

Alright, enough of that. So far we’ve been defining a number of top-level variables, but your functions will need to have types as well in order to indicate the types of arguments they accept, and what values they return.

As an example, let’s create a super simple function that tests whether a given number is a magical number.

We’ll say that if the number we pass in is 5, then it’s a magical number, otherwise it’s not magical.

And you’ll see TypeScript is complaining at me here, saying “parameter num implicitly has type any”, which is a fancy way of saying I haven’t told it what type num is, so it is forced to assume that it could be anything.

We can remedy this using syntax identical to what we used above. After the variable name, I’ll simply put a colon and the type of the variable, which is number, and now TypeScript is happy with us again.

Not only is TypeScript happy with us, but if we go to use this function, our text editor can use the definition to display what the expected types of the function arguments are.

Another thing you can do is explictly specify what the return type of your function should be. We do this in nearly the same way as declaring variables, by placing a colon after the closing parenthesis and then the type.

So, for example, if we were like hey we should have been returning a boolean, we can specify that, and then TypeScript will let us know anywhere that we are returning the wrong value.

In general as I have said before, TypeScript is going to be really good at inferring what the types of things are. So, here we could have gotten rid of the return type and TypeScript would’ve correctly guessed that boolean was what we wanted.

However, I recommend you always type your arguments in your return values for functions, that way it’s really clear to you and your team members that the function is operating in the way you intended it to operate. And it’s really easy to just glance at the line of code where you create the function and get an idea of what you expect it to do.

One other type I wanted to mention that is related to functions is called the never type.

It’s an peculiar type that means a function will never return. The two main examples of this are functions that you call to throw an error, or functions that create an infinite loop.

For example, something you’ll see in many code bases is is a function named “fail” or “panic” or something similar. This is the function you call when things have gone horribly wrong and you need to log an error and end the program.

Since the program dies before the function fully finishes, the never type is very appropriate. It’s never going to return. You can indicate that this is the case for a particular function by specifying “never” as the return type.

Likewise, if you have a function containing an infinite loop, that will also have a return value of never. The loop never stops, so the function will never return.

Keep in mind that “never” is distinct from a function that returns no value at all. For example, many times when you’re working with graphics for a game or something else, you’ll have some code that updates those graphics on-screen. There isn’t much information this code needs to return anywhere, so it’ll successfully successfully draw the graphics, and then return nothing. For that case, TypeScript has a “void” type, which you can use to indicate that the function will return at some point, but it’s not going to return any value.

That’s it for how to use basic types, and that wraps up this video.

If you’ve enjoyed what you learned 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 a the bottom of the page to be notified when it’s ready.

Thanks so much for watching! See you in the next video.

Learn TypeScript #1, The Basics of How Types Work Pt. 1

Transcript:

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”

We’ll change one line for now, we’ll uncomment “lib” and add “dom” and “es5”. This will let us reference objects that exist when running JavaScript in a web browser, which I’ll be using for examples later.

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.

We’re going to start off by specifying types on simple variables. There are three ways to declare variables in TypeScript and JavaScript: var, let, and const. In terms of specifying types, they all work the same, so for this video I’ll simply be using “let”. In a future video I’ll cover the differences between var, let, and const, but like I said, for this video, it doesn’t matter.

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 is a good time to mention that in the same that way we created and used our own types above, we can use existing types that are part of JavaScript or JavaScript’s environment. For example, because we included “dom” in our “lib” in tsconfig.json, we can create a variable and declare that it’s type is HTMLElement. We could then fill this variable with something from the DOM API, like document.getElementById.

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.

“any” is exactly what it sounds like. If you say the type of your variable is “any”, you’re saything that it can hold any value possible. Generally if you’re using TypeScript, I strongly recommend against reaching for “any” unless you absolutely have to use it. Though if you’re interacting with non-TypeScript code, it can be appropriate to use “any” in order to deal with JavaScript libraries that truly can produce any value.

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.

What is TypeScript?

Transcript:

So what is TypeScript?

At a super high level TypeScript is JavaScript with static type checking. What that really means is that you no longer have to write JavaScript code purely based on faith and good guesses that the variables and objects you’re using are what you think they are.

TypeScript is going to come along side you and enforce that “hey this is a string this is a number this is you know some object with a certain felds” TypeScript is going to add those guarantees so that you can code with more confidence.

In practice you’ll find that you’ll have fewer dumb errors; If you’re working on a team, it becomes vastly easier to communicate what certain code does because they can see right there what the types are and when they go to use them TypeScript is going to tell them if they’re using them erroneously.

It is easier to communicate with your future self so that when you come back to your code it’s easier to tell how things work. You can just look and see what the types of various objects are without having to go back and actually run the program to see if the guesses you’re making about the program are actually true.

In short, the code has become much more self-documenting

If you go to the TypeScript website you’ll see Microsoft describes TypeScript as “JavaScript that scales”. What does that really mean?

What it has meant in my experience is that you can bring a team of people together or you can work on something yourself and you can build programs and applications that very large and complex and you’ll going to have much more confidence in what your building and in and how you’re managing the complexity then you would be if it was just plain JavaScript.

I can recall numerous times on plain JavaScript projects before we started using TypeScript that I would go to refactor something or move some code around or change some logic or whatever and I could never be entirely sure that I hadn’t broken something. Even if my tests pass even if in my manual testing everything was working right, I was never able to be fully confident in the changes I had made. It seems like there would always be something flew under the radar that got missed.With TypeScript that happens far far less often, in fact I’d say almost never.

TypeScript is a strict superset of JavaScript. What that means is that anything you know about JavaScript you can transfer into your knowledge of TypeScript. There are definitely new things to learn, but it’s not a whole new world that you’ll be completely lost and confused in. If you already know JavaScript, you’ll be good to go.

In a lot of cases you can take code that you’ve already written in JavaScript change the file extension from .js to .ts and everything will work, or most of it will work with maybe a few extra type annotations that you need to add in. So it’s very straightforward to get started and to jump in.

Another great thing about TypeScript is that it compiles directly to JavaScript. In practice that means that you don’t have to install any extra runtime or ship any additional library to your users in order to start using TypeScript in your application. In development, you’ll just run the TypeScript compiler and then away you go.

Since it’s just plain JavaScript you can use the same debugging tools that you’re already used to. You can also graudually introduce TypeScript into an existing JavaScript project, slowly converting files to TypeScript bit by bit as you go along.

If you’ve used other languages that compile to JavaScript, you might be used to the compiled code looking unintelligible, very much like a machine wrote it. In most caess, TypeScript is merely erasing your type annotations, so your original code remains. In cases where it does have to manipulate things, I’ve found it makes very readable choices about the code it produces.

Anothing great thing about TypeScript too is that it supports many of the new features that have been added to JavaScript in ES6 and other newer JavaScript versions. Not only that, but you can have TypeScript compile to older JavaScript versions so that you can use the latest features in TypeScript, but still support older browsers.

One thing I wanted to mention as well is that the types are in a lot of cases optional, or what you might call a gradual, or can be inferred by TypeScript. That means there’s a lot of cases where you don’t really need to tell it what the type of something is, it’ll see “hey I’m setting this variable to a string so that must be a string”, and you don’t have to do the redundant typings that you might have gotten used to if you came from a C# for a Java background, and that’s really nice.

Also, depending on the configuration flags you set and how much you use the catch-all “any” type, you can use the type checking as much or as little as you want. If you want it to be super strict and have types everywhere, you can, or if you want to write JavaScript in the more dynamic untyped way that you’re used to, you totally can.

I think TypeScript helps enforce some good habits in a few different ways. First, once you get used to the typings, it becomes very natural when writing new code to sketch out the interfaces and relationships between data before you start writing code, because you can use those right away when you start actually implementing your program, rather than just leaving them as inert comments or notes or thoughts in your head.

Second, depending on how you configure TypeScript you can make it so it’s much more strict about checking for null and undefined, which is a really good habit to get into.I’ve been guilty myself of getting lazy and thinking “well this could fail as null or undefined, but I’m just going to ignore it because it makes my code a lot simpler.” But if TypeScript is yelling at me I’m much more likely to do the proper thing and that’s really good.

Third, if you’re defining a type and it becomes hard to think about or it hard to describe a lot of times that’s been an indication to me that “hey I’m making this more complicated than it needs to be” or I’m I’m trying to do this in a way that might be clever now but it isn’t going to stand up to the test of time. I’m going to come back to this later be super confused or I’m going to have to hand this off to a co-worker and they’re going to be super confused.

Better IDE support is one thing that’s great is if you’re using something like Visual Studio Code or JetBrains WebStorm or any other IDE or Smart Text Editor with TypeScript. You get way better auto completion so you can access things or call functions and it’s going to tell you “hey this is how you call this thing” or “hey that’s an error you missed a parameter here” and that kind of thing.

If you’ve used just plain JavaScript in the past, you’ll know that IDEs can be pretty good about guessing what things might be, but there’s a lot of times where you’ll start typing a piece of code and it does the auto-completion and gives you every possible field name that I’ve defined in my project as an option. Which is massively unhelpful. With TypeScript you avoid that issue entirely, since it knows what the types of things are, you get vastly better auto completion support. Which makes you a much happier and more productive programmer.

So that’s a general overview of what TypeScript is. I hope that was helpful to you! I’ve got other videos out there on TypeScript, so if you want to learn more, please check them out!

If you visit https://typescriptbyexample.com you’ll see some info on a course that I’m building for TypeScript. You can put your email address in at the bottom of the page and you’ll be notified when that’s ready. I’d love to hear from you, and I’d love to have you be a part of the course. Thank you so much for watching! Goodbye!