What do you want to learn?
Skip to main content
by Brice Wilson
TypeScript is a modern language with many advanced features. This course will teach you those features that build on the fundamentals you already know and allow you to harness its full power to write better code with fewer errors.
Start CourseBookmarkAdd to Channel
Table of contents
Hi, this is Brice Wilson with Pluralsight. Welcome to Advanced TypeScript. In this course, I'm going to help you take your TypeScript skills to the next level. Maybe you've experimented with some basic features of the language and want to explore some of its other capabilities, or maybe you've been using it for a while and find that you now have a need for some of its more advanced features on your project. In either case, I think this course has just what you're looking for; and we'll cover lots of powerful features you won't normally see in an introduction to the language. This course is really a follow-up to my earlier course titled TypeScript In-depth. That course was for complete beginners with no TypeScript experience. In it I covered all of the basics needed to get up and running with TypeScript and presented lots of beginner and intermediate language features like functions, classes, interfaces, modules, generics, and lots more. That course is certainly not a requirement for this course, but if you're new to the language; or just need a refresher on some of those topics, then that would be a great place to start. I also used the same simple demo project in both courses, so I hope that makes moving between that course and this one a little more seamless.
From Beginner to Advanced
I want to briefly make the case for obtaining advanced skills. Learning something new in my experience, seems to follow a very consistent pattern. Initially it can be very frustrating. Everything is new and even the most basic things may not make much sense. However, stick with it and usually within a reasonable amount of time you'll have that aha moment when everything suddenly makes sense. You're no longer struggling with the simple stuff, and you start to see the big picture. This is usually followed by a period of tremendous productivity. You're proud of what you learned, you're applying it on a project, and you're getting work done. This all feels great, and you should definitely enjoy it and build lots of cool stuff. However, there are warning signs to watch out for. Being proficient is usually good enough to be productive, and being productive feels so good that it's easy to stop pushing yourself to learn more and continuously get better. Just like athletes have to keep training and pushing themselves to reach new levels of fitness, you have to keep pushing yourself if you want to make that jump from proficient to advanced. I think the extra effort is worthwhile. Learning the advanced features of any given technology often opens up new ways of looking at problems and all of your new advanced skills add up to new solutions you may not have even thought possible if you stayed in your comfort zone of mere proficiency. Push yourself to go from proficient to advanced, and the things you'll build will be even better. That's what I hope to help you do with this course. Let's now take a quick look at what I'll cover in each module to help you in that journey.
In the next module I'll show you how to do more with the basic types you're already familiar with. I'll show you some new tricks with arrays and how to use existing types to create union and intersection types. After that I'll show you some more advanced type features, such as how to create polymorphic disk types and type guards. I'll then have an entire module dedicated to creating and using decorators. They're a very powerful feature and are used heavily in popular frameworks like Angular. After that, I'll show you several ways to manage asynchronous code in TypeScript. This will include using traditional call-back functions, as well as promises and the much newer async and await keywords. I'll then wrap up the course with a short module on a popular linter for TypeScript. Linters check your code for errors and adherence to coding standards. TSLint is easily the most popular TypeScript linter and does a great job of helping you write better code. The last thing I want to do in this quick introductory module is a quick demo to show you the app I'll be working with throughout the course and the tools I'll be using to run it.
Demo: Project Structure
Going Further with Basic Types
Introduction and Overview
Hello and welcome back. I'm Brice Wilson. In this module, I'm going to show you how to do more with the basic typescript types you are already familiar with. I will cover a couple of nice ES 2015 features typescript supports and then show you several ways to take the types you already know how to create and combine them in new ways to create entirely new types that ultimately allow you to write more flexible code. I will begin by showing you how to perform destructuring assignments and how to use the spread operator. These are both new ES 2015 syntax features that the typescript has adopted. After that, I will show you how to create and use tuple types, union types, and intersection types. They may sound like completely new things but they are really just different ways to combine existing types to make your code more flexible and expressive. After explaining intersection types, I'll show you how to create mixins which allow you to combine all of the members from multiple classes into a single class. I'll then show you how to create string literal types and how to define type aliases that can make your code more readable as you begin to use some of the other features like union types and intersection types. I'll start things off in the next clip by showing you how to perform destructuring assignments.
Destructuring Arrays and Objects
In this section, I'll show you the new syntax available for destructuring arrays and objects. Let's start with a definition. A destructuring assignment is the process of assigning the elements of an array or the properties of an object to individual variables. You've always been able to do this of course but there is now syntax specifically designed to do this for you in a clean and concise format. Let's look at a couple of examples. Let's imagine I have a simple array of strings named medals. It contains three elements with the values gold, silver, and bronze. If I wanted to put each of those values into distinct variables, I could just declare a new variable and index into the array to get the value to assign to the variable. This works fine and the result will be that the variable named first will have the value gold. Second will have the value silver and so on. The new destructuring syntax lets me perform those same declarations and assignments in one line of code. The new variable names are listed inside square brackets on the left side of the assignment and the array to be destructured is on the right side. The values in the array elements will be mapped to the variables in the corresponding array positions on the left. For instance the value in the first element of the medals array will be assigned to the variable in the first position of the array on the left side of the assignment. You might wonder how to deal with any extra values if the array you are destructuring contains more values than you expect. That can be handled with the new rest parameter syntax. I'll show you how to use that in a demo in just a minute. Let's first see how to destructure objects. Destructuring objects is nearly identical to destructuring arrays except that instead of assigning elements of the arrayed individual variables, you are assigning properties of the object of those variables. For example let's say I have an object literal named person that has name, address, and phone properties. I could easily declare new variables and use that dot syntax on the object to get the value of the property and assign it to the new variable. However, with the new syntax I can accomplish the same thing with this much simpler code. It looks similar to the array example earlier but notice that the new variable names are listed inside curly braces instead of square brackets on the left side of the assignment. The right side simply contains the object variable. Also notice that the names of the variables match the names of the properties on the object. That's how it will be determined which property on the object will match to which variable since the properties on an object aren't ordered like the elements in an array. There is a slightly different syntax you can use if you want to use variable names that don't exactly match the property names on the object. I will show you how to do that in the next demo.
Demo: Destructuring Arrays and Objects
The Spread Operator
The spread operator is another new bit of syntax typescript adopted from ES 2015. The spread operator gets its name from its ability to spread the elements of an array across the elements of a new array or even a set of function parameters. In this simple example, I have an array of newBookIDs. It just contains two elements with the values 10 and 20. I'll then add them to the end of this array named allBookIDs using the spread operator. To do that, I just prefix the name of the array with three dots. The elements in that array will then be spread across new elements in the current array. Since the array already has three elements, the two elements in the newBookIDs array will be spread across the fourth and fifth elements resulting in an array that looks like this. You can also use the spread operator to spread elements across function parameters. I'll show you how to do that in the next demo.
Demo: Using the Spread Operator
In this demo, I'll show you how to use the spread operator to pass function parameters and declare array literals. I'm going to start here in app.ts by using a code snippet to paste in a new array of books. I'm assigning it to a variable named schoolbooks and it is just an array with three object literals representing books. I want to show you how I can use the spread operator to add the elements in this array to another array. I will sign the array of books return from a call to GetAllBooks to a new variable named BooksRead. I'll now call the push function available on all arrays to add all of the school books to the booksRead array. You can see here that the push function is defined with the rest parameter. That required for me to call it with a spread operator and not have the compiler complain about the function signatures not matching. I'll use the spread operator which are the three dots and the schoolbooks array is the parameter to the push function. The elements in the school books array will be spread across the rest parameter and added to the booksRead array. I'll log the updated array so we can see that the books got added. If I quickly run the code, you can see that the BooksRead array now contains the new elements from the schoolbooks array. I've got to scroll a bit to see all of them. I will show you how you can use the spread operator to append items to array literals. I'll comment out the log statement just so we don't see that output anymore. I'll declare a new of array of strings named poets and assign it in array of the names of several poets. I'll declare another string array named authors. I'll also assign it an array literal. The first two elements will be author names but then I'll use the spread operator to spread the elements from the poets array across the end of this array. I again just used the three dots in front of the array variable name. I'll log the authors array, run it, and you can see that the array does in fact contain the names of the poets in the last three elements. The spread operator is a pretty simple but handy feature. Let's now go take a look at tuple types.
In this section, I'm going to present tuple types. They are a special type you can create that can be a useful building block for other types. The typescript language specification defines a tuple type as a type that combines a set of numerically named properties with the members of an array type. They weren't very much like arrays but have a couple of unique features. Their extensions to arrays were the type specified for a fixed number of elements and the elements may contain different types. In this example, I've declared a new variable named my tuple that is a tuple type and I'm assigning it a literal value. The type is specified after the variable name like any other type annotation. Tuple types list the type that is allowed in each element of the array inside a pair of square brackets. The first element of this array must be a number and the second element must be a string. I can manipulate the values in the tuple type using indexes like I would for any other array. If I were to try to assign a string value to the first element in the array, I would get a compiler error since the type specifies the first element must be a number. Similarly, I'll get an error if I try to assign anything but a string to the second element. Things are a little different for elements in the array beyond those with explicitly specified types. They may contain any of the types listed as part of the tuple type. In this example, that means the third element can contain a string or a number since those are the two types in my tuple type. Let's now jump into a demo where I will show you some additional examples.
Demo: Creating and Using Tuple Types
I'll now show you an example of using tuple types as a way to describe key value pairs in typescript. I'm still here in app.ts and I'm going to create a simple tuple type to represent the location of a book on one of the library shelves. I really know nothing about how library catalog systems work so please forgive me if you do and my catalog location makes no sense. I'll declare a new variable named catalog location and specify that it will be a tuple type where the first element is a string which will represent the shelf location and the second element will be the actual book at that location. I'll assign it an array literal where the first element is a crazy string representing the location and the second element is the book stored in the book1 variable I created above in an earlier demo. This syntax is valid and I'm not getting any complaints from the compiler because the types in my value array match the types on my tuple type. There is a string in the first element and a book in the second element. If I change the value in the first element to be a number, I immediately get an error. I'll hover it and the compiler lets me know that types of property zero are incompatible. Type number is not assignable to type string. This makes it obvious that the compiler was expecting a string and I'm giving it a number but I want to explicitly point out that it is referring to the property named zero. If you recall the definition of a tuple type I showed you in the slides, it is a type that combines a set of numerically named properties with the members of an array type. Property zero is the numerically named property for the first element in the array. I'll show you another example of using these properties in just a minute. I'll change the value in the first element back to a string to get rid of the air. If I change the value in the second element to something other than a book, I get a similar error but this one refers to the incompatible types for the property named one since it is the element with the index of 1 in the array that has a type mismatch. I'll change that back to the book variable. I can additional elements to the array but the type stored in those elements must be one of the types to find on the tuple type. Therefor if I try to assign a Boolean to the third element in the array I'll get an error. However, if I change it to a string the error goes away because the string is one of the types in my tuple type. You may have noticed that my tuple type closely resembles a key value pair. The first element is a string and could be the key for the second element which is a value, a book in my case. Creating key value pairs are an excellent use for tuple types and is one of the examples provided in the typescript documentation that I want to show you now. I'll comment out this last bit of code so I can write a better version of it. I'm going to create the exact same interface used as an example in the typescript documentation. It is a generic interface named key value pair. It takes two type parameters named K and V. K represents the type of the key and V represents the type of the value. Because tuple types are extensions to arrays, this interface will extend an array that contains elements with the types assigned to K or V. This is the syntax for a union type, which we will look at more closely later in this module. The next step in creating this custom tuple type is to define the numerically named properties we saw earlier. I'll define on the interface a property named zero that will have the type assigned to K. I will define another property named one that will have the type assigned to V. I'll now rewrite my earlier example of a catalog location using this new custom tuple type. The variable will be a key value pair and the type parameters will be string and book. I can assign a value to it exactly like I did before. The first parameter is a string which maps to the type parameter K which must be the type of the property named zero. I'm not getting any errors because I've correctly assigned a string to that property. The same applies to the book I am using for the value in the key value pair. If I change either of the values so that they no longer match their assigned types, I will get an error. I can add new elements to the array as long as they match one of the valid types. Boolean still won't work since this tuple type is specified to contain strings and books, but all is well if I assign a string to the third element. Tuple types can be handy as a way of composing new types from existing types and I think this key value pair example from the typescript documentation is the best example of that. Let's now go back to the slides and talk about union types and intersection types.
Union Types and Intersection Types
In this section, I'm going to show you how to use union and intersection types. They are both a way of describing a type in terms of other types. I'll also show you how to create a mixin which is a technique for implementing an intersection type. Let's start by taking a look at union types. Union types provide you with a way to specify that a value will be one of several possible types. In this example, I specified a union type for the function parameter. It can accept a string or a number. I could have simply said that the ID parameter would have the any type but colors would obviously be able to pass anything to it. If I know that number and string are the only valid types it is better to restrict it to just those types so that passing anything else will result in a compiler error. The list of valid types are separated by a vertical bar. It resembles the logical or operator which is two vertical bars. I think that provides a nice hint as to its purpose in an union type. The type of the value can be a string or a number. I only listed two possible types for this parameter but you can list more separated by additional vertical bars if you need more. Intersection types are similar to union types except that instead of describing a value as having one of several possible types, an intersection type specifies that a value will have all members from multiple types. In this silly example, I've got a function named CreateCoolNewDevice. That returns an intersection type. The return type will have all of the members from both the phone and tablet types. The types that combine to form an intersection type are separated by an ampersand. This is your hint that you are looking at an intersection type. The logical and operator is represented by two ampersands. Intersection types are just separated by one but they contain all of the members from the first type and the second type.
Demo: Using Union and Intersection Types
In this demo, I'll show you some examples of using union and intersection types in the library manager app. Before I start creating the new types, I want to get some books and magazine objects so I can use in the examples. I will create a new variable named allBooks, and assign it the array of books returned from the GetAll books function. I will do the same thing for magazines. There's another function in the utility module named GetAll magazines. I'll assign each return value to all magazines. I'll now declare a variable named reading material. I want to be able to assign it either a book or a magazine. I could declare its type to be any. That would let me assign any value even a number like this that doesn't really make sense for how I intend to use the variable. However, it does satisfy my need to assign it both magazines and books. As you can see, I can assign it the first element of the allmagazines array without any errors. I can do the same thing with the allBooks array. The any type is really too forgiving. It allows me to do what I want but doesn't prevent me from doing things I shouldn't. I could change the type to book and it obviously won't let me assign it a number but it also won't let me assign it a magazine. The answer to this problem as I'm sure you've guessed is to use a union type. I'll use the vertical bar between the types I want to allow for this variable. When I add the magazine type to the union type you can see the error goes away. I can also assign it a book without any errors. Union types can also be used as the type of function parameters. I'll quickly write a new function named printTitle that accepts the union type. I declare the type of the item parameter just like I did the reading material variable above. Inside the function, I'll just log the title of the item since both books and magazines have a title property. I can call the function by passing it a book like this. I can also pass it a magazine. However if I try to pass it a string literal, I'll get a compiler error letting me know that I can't assign a string to a union type including book and magazine. Okay I'll get rid of that line and comment out the previous two. I'll now declare a new variable that will be an intersection type including book and magazine. I'll name it serial novel. Intersection types must include all of the members from the types in the intersection. Therefore if I try to assign a book to this variable I get an error. That's because the book type doesn't include a publisher property. Magazines have a publisher property which means anything I assign to this intersection type must also include that property. I'll change this and use a code snippet to assign an object literal to the variable. This object contains only the properties on the book interface, so I get the same error I got before. However, if I just add a publisher property to the object. You can see that the error goes away. The value I'm assigning now includes all of the properties from all of the types included in the intersection type.
Demo: Creating a Mixin
Mixins are effectively an implementation of an intersection type. In this demo, I'll create mixins that add some additional functionality to the university librarian class in the demo app. I'm going to start here in the classes.ts file and create the mixins I want to add to the university librarian class. Mixins are really just classes whose functionality you add to some other class. I'll first use a code snippet to paste in a new class named employee. It has a string property to store the employee's title as well as functions named add to schedule and log title. I have them each logging a simple message to the console. I use another code snippet to paste in a class named researcher. It contains a single function named do research which accepts the research topic as the strength. Before I do anything else, I am going to jump down to the end of this file and add each of the new classes to the list of things being exported from this module. Okay I now want to add the functionality from both of these new classes to instances of my university librarian class. In practical terms, I want to inherit the functionality from employee and researcher. However typescript doesn't allow you to inherit from more then one class at a time. The technique I'm about to show you let's you work around that limitation. The syntax here is probably going to look a bit strange but stick with me and I will make sense of it. The university librarian class currently implements the librarian interface. I'm going to add a comma after that interface and add the employee and researcher classes to the list of interfaces university librarian should implement. The thing that may seem immediately wrong about this is that employee and researcher are classes and I'm using them here like interfaces. Classes are normally things you extend but here I'm specifying that I will implement them. The typescript compiler doesn't complain about this and is willing to treat the classes as interfaces. However that means that the university librarian class is responsible for implementing the members on those classes even though they obviously already have implementations. I'm going to write a function in just a minute that copies the implementations from those classes into the university librarian class, but for now I just need to add the member definitions to satisfy the compiler. The red squiggly here is because I'm not fully implementing the members from employee and researcher. I will come down to the end of the class and use another code snippet to paste in very basic definitions for those members. You can see that I've pasted in the title property and the two functions from the employee class as well as the do research function from the researcher class. I'm not providing any implementation here. I've just defined the members and given them types and function signatures to match those and the source classes. I'm going to write a bit of code to copy the original implementations into instances of the university librarian class. I will do that back in the app.ts file. The first thing I need to do in this file is add employee and researcher to the list of things I'm importing from the classes module. I'll now come down to the end of the file and paste in the function that will add the implementations and my mixins to some other class. This is the same function you will find in the typescript documentation that shows you how to implement mixins. It is a short function but it performs the magic of adding the additional functionality to the classes we specify. It takes two parameters. The first is the constructor function or class to which you want to add functionality and the second is an array of constructors containing the functionality you want to add to the first. The function just iterates over the items passed in the second parameter gets the members from those classes and added them to the prototype of the class passed as the first parameter. To apply members from the employee and researcher classes to instances of the university librarian class I just call the function and pass university librarian as the first parameter and an array containing employee and researcher as the second parameter. The actual implementations will be added to university librarian when the function is called. But I'll get code completion support for all of the new members because I specified the class would implement the members from employee and researcher. Now when I create a new instance of the university librarian class and type a dot on the new instance, you can see all of the members from the mixins along with the members defined directly on the university librarian class. I'll call the doResearch function on this instance just to show you that the apply mixins function above did in fact copy the actual function implementations from the original classes. I'll run the code and I get the simple output message I generated in the researcher class. Mixins can be a nice way to combine functionality from lots of small classes into a carefully crafted larger class with just the functionality it needs. Let's now wrap up this module with a look at string literal types and type aliases.
String Literal Types and Type Aliases
The last couple of topics I want to cover in this module are string literal types and type aliases. They are both relatively small features but I really like them and think they can help you write simpler more readable code. String literal types are exactly what their name implies. A string literal treated as a distinct type. I realize that sound strange but I think you'll see their potential usefulness as I show you how they can be used. They are specified just like any other type annotation. At first glance, you might think this category assigns the string manager to the emp category variable but if you look close you can see that there is a colon after the variable name indicating that the next bid is the type of the variable. Variables declared with a string literal type can only contain the strings specified in the type. Therefore, my emp category variable can only be assigned the string manager. If I try to assign it any other string like non-manager the typescript compiler would generate an error. So far these types surely don't sound very useful. Their value becomes more apparent when they are combined to form a union type like we looked at earlier. Notice the vertical bar between the two types indicating this as a union type. I've unioned together two string literal types so that the variable can contain either of two possible strings. Manager or non-manager. Using a single string literal type is not particularly useful but unioning them together like this gives you enom like behavior that can be useful when you want to limit some value to a finite set of strings. I will now use the same union type to show you how type aliases work. Type aliases are just a way to refer to a type by a different name. You probably wouldn't decide you need a new way to refer to numbers or Booleans but you might decide an alias would be helpful for more complex types like the union type from the last slide. Unioning together two string literal types is long hard to talk about and generally kind of clunky feeling. I think that makes it a perfect candidate for a type alias. I'll create an alias for it named employee category. You create an alias using the type key word followed by the alias you want to create. You then set it equal to the original type. With that in place, I could rewrite the variable declaration above like this. Rather than writing out the entire union type, I just give it the employee category alias for that type. I think that's much easier to read and works just like the original. I can assign the value manager or non-manager to the variable and those are the only acceptable values. Trying to use any other strings will result in a compiler error. Let's now go experiment with string literal types and type aliases more in a demo.
Demo: Using String Literal Types and Type Aliases
In this demo, I'll create a new string literal type and define several type aliases for types I've used throughout this module. I'm first going to use a string literal type to describe how often a magazine might be published. I'll create a new variable named frequency and I'll combine two string literals into a union type to limit the possible values the variable can have to monthly and annually. I can assign values to the variable but only if they are one of those two strengths. No complaints from the compiler if I assign it the string monthly. It also accepts annually. However, if I change annually to semi-annually then I get the dreaded red squiggly under my variable name. Type semi-annually is not assignable to type monthly or annually. I'll change it back to annually to get rid of the error. The primary benefit of a language like typescript is your ability to express types for things based on what you know about the range of acceptable values. The compiler then alerts you if you try to use values outside that acceptable range. Being able to limit the type of a variable to a string is nice, but if you know that variable should only ever contain one of a small number of strings then it is even better if you can have the compiler check your usage against that small set. That's the benefit of using string literal types. Unioning these two string literal types together produces a new type I might like to use elsewhere in the application. So I think I will create a type alias for it to make it easier to type and a bit easier to read. I'll comment out this variable declaration and then use the type keyword to define a type alias named frequency. I'll set it equal to the same union of string literal types I used above. Let's now suppose I needed a new function that would return a magazine based on how frequently it is published. I'll stub one out named getMagazinebyfrequency. I'll pass it the preferred frequency and use my new type alias to describe the type of that parameter. As you can see, once you've declared a type alias you can generally use it like any other type annotation. I'll now create a couple more type annotations that I can use to make some of my earlier code a little more readable. I will create one called PrintMaterial that will be an alias for the union of the book and magazine types. I'll create another one named serial that will be an alias for the intersection of those same types. I can now go back to the places above where I used those types and refactor them to use the new aliases. I declared this reading material variable to be the union of book and magazine so I'll change it to print material. I can do the same thing for the parameter I'm passing to the PrintTitle function just below that. Finally, I can use the alias I named serial on this variable whose value must be the intersection of a book and magazine.
Whenever you learn a new piece of technology, you are usually focused on the fundamentals and understanding the basic used case for the technology. Initially you will probably only use the basic features. It is easy for your growing comfort with those features to keep you from ever exploring more advanced features and capabilities. In this module, I tried to take some of the basic typescript features you already know and show you some of their capabilities to go beyond what you probably learn in your early experimentation with the language. We all know how to declare a variable and assign it a value but I showed you how you can use destructuring assignments with arrays and objects to accomplish the same thing with less code. I also showed you new ways to work with arrays including using the spread operator and extending arrays by creating tuple types. I then demonstrated several ways you can combine existing types to make your code more flexible. We looked at union types, intersection types, and mixins. Finally I showed you how to use string literal types and type aliases which I think are both handy features to help you keep your code clean and readable. In the next module, we will keep working with types but look at more advanced features like creating custom type guards, declaration merging, and creating fluent APIs. Stay tuned.
Using Advanced Type Features
Introduction and Overview
Polymorphic this Types
In this section, I'm going to cover polymorphic this types and show you how they enable you to build a fluent API in TypeScript. Let's start with the definition. A polymorphic this type represents a type that is the subtype of the containing class or interface. What this really means is that the type of the object referenced by the keyword this may be the type of an inherited class or interface. I know that it still sounds a bit abstract, so let's look at an example. Let's imagine that I have a class named Vehicle and then it has a method named Drive. It does some work, and at the end of the method, it returns the value referenced by the keyword this. This is the this that is polymorphic, meaning that it can represent more than one type. If I were to write some code like this that declares a new instance of my Vehicle class and then calls the Drive method, the type of the value returned by the method would be Vehicle. However, let's now suppose I create a class named Car that extends the Vehicle class. It's a subtype of Vehicle. It has its own method named CarryPeople but it also inherits the Drive method from the Vehicle class. If I create a new instance of it and call the Drive method, the type of the value returned in the this reference will be Car. Similarly, I could create a second class that extends Vehicle named Truck. If I call the Drive method on a Truck instance, the type returned by this will be Truck. It's polymorphic because depending on the context, it could be a Vehicle type, Car type, or Truck type. One of the things this enables is the ability for us to build hierarchical, fluent APIs. I'll show you an example of that in a demo next.
Demo: Creating a Fluent API with Polymorphic this Types
In this demo, I'm going to take advantage of polymorphic this types to create a fluent API for books in the Library Manager app. I'm here in the app.ts file and I've removed the demo code from the last course module just so we can start this module without quite so much clutter. You'll still be able to get all of the code from the last module in the downloadable course materials. I'm going to start by creating a simple new class named LibraryBook. I use a code snippet to paste in the body of the class which includes two methods, Checkout and Checkin. In a more realistic implementation, they would implement some business logic related to the process of checking in and out books from the library. This simple version just logs the action that's taking place. The more important piece of the code though is really the return type of each method. Notice that I specified this as the return type for both and then I explicitly returned this on the last line of each method. As we saw on the slides, it's the polymorphic nature of this that's going to enable us to create a fluent API using the LibraryBook class. I'll come down a couple of lines and paste in another class named ChildrensBook that extends the LibraryBook class above. Since kids often color in books and generally treat them a little rougher than adults, I've added a method of this class called Clean. Just like the methods in the parent class, this method's return type is this and I'm explicitly returning it on the last line. I'll now create a new ChildrensBook instance and show you how I can use it. A fluent interface or fluent API is an implementation that allows you to chain methods together, which hopefully produce more readable code. This works only if the method returns the object instance on which the methods exist. I accomplish that by returning this in the methods above. Let's suppose the kidbook instance I have here has just been returned to the library, but I need to immediately check it out to another customer. I would first call the Checkin method. That method returns this, which, in this case, is an instance of a ChildrensBook, so I can call the Clean method on it. We want the books to be clean before checking them out to someone else. The Clean method also returns this, so I can immediately call the Checkout method. Having each method return this allows this type of method chaining. You could chain them together on one line like this, but my personal preference is to put each method call on its own line. I, again, want to explicitly point out the polymorphism in this example. It's the LibraryBook class above that contains the Checkin method. That method returns this, which would normally be a LibraryBook instance, which does not include a Clean method. I'm only able to call Clean here because the type of this returned from Checkin will actually be a ChildrensBook since that's the type of the variable I created. The this type is polymorphic because it can be either a LibraryBook instance or a ChildrensBook instance, depending on the context in which it's used. I'll quickly run this just so you can see it works as expected. I'll first start the build test that compiles the code and then I'll press F5 to run it. You can see the expected log statements from each of three methods in the console. I'll now make it a little more interesting by pasting in another new class that extends the LibraryBook class. This one is called ElectronicBook and includes a method named RemoveFromCustomerDevice. Just like the methods in the other classes, the return type is this and I explicitly returned this at the end of the method. I'll paste in an example below that creates a new ElectronicBook instance and chains together several method calls on it. The important part here is that I'm able to call the RemoveFromCustomerDevice method on the type return from Checkin method. The type of this return from Checkin in this example will be ElectronicBook instead of LibraryBook. Before I run this, I'm going to tweak the code in the Checkin method so that it outputs the type of this during each call. I'll comment out the existing log statement and paste in a couple of conditional checks. In the first if block, I'm using the instanceof operator to see if the type of this is a ChildrensBook. If it is, I just log a message to that effect. I do the same type of check just below that to see if this is an ElectronicBook. These checks should make it very clear that the type of this is context-sensitive. I'll run the code one more time, and you can see in the output that the type of this inside the Checkin method is different for each instance. In the first example, it's a ChildrensBook, and in the second, it's an ElectronicBook. Okay, let's now go back to the slides and talk about declaration merging.
Demo: Interface Merging and Module Augmentation
In this demo, I'll first show you a simple example of merging interfaces. I'll then show you a more practical example of using declaration merging to augment a module. I'm going to start with an example of interface merging so I'm here in the interfaces.ts file. This file contains the Book interface I've been using the course. Just below that interface, I'm now going to create another one that also has the name Book. I'll give it two properties, publisher will be a string and hasIndex will be a Boolean. Because these two definitions have the same name, the TypeScript compiler will merge them into a single interface. This is easy to see when you attempt to use the interface. I'll go back to app.ts and create a new variable named mergedBook that is a Book type. If I now type a dot after the variable name, the code completion support clearly shows all of the members from the merged interface definitions. You can see that the publisher and hasIndex properties have been added to the list of members available on the original interface definition. I'll choose the publisher property and assign it the string. Let's now take a look at what I believe to be a more practical example of declaration merging. It's known as module augmentation and is a technique that allows you to extend existing modules with new members. It's a nice way to extend modules you might not maintain. I'll quickly jump over to the classes.ts file and show you the UniversityLibrarian class. It contains a few properties and a few methods, but I'm now going to build an extension to the class that will be merged with this original definition at compile time. I'm going to create the extension in a new file. I'll name the file LibrarianExtension.ts. The first thing I need to do in the new file was import the UniversityLibrarian class from the classes module. In order to augment or extend that module, I need to declare a new module with the same name. Inside that declaration, I'll create an interface named UniversityLibrarian and give it a phone property and a method named hostSeminar that takes a topic as a parameter. This interface will be merged with the UniversityLibrarian class definition and new instances will have all the members from both definitions. The last thing I need to do is provide an implementation for the hostSeminar function. I'll do that by assigning a function to the hostSeminar property of the UniversityLibrarian prototype. That's all I need to do. These new members will now be merged with the original class by the compiler. I'll go back to app.ts and create a new instance of it. I first need to jump up to the top of the file and import the contents of the new LibrarianExtension file. I'll now create a new variable named newLibrarian and assign it an instance of UniversityLibrarian. When I type a dot after the variable name, you can see that this instance includes the members from both definitions. I've got all of the original members as well as the phone and hostSeminar members. I'll assign a value to the phone property and then call the hostSeminar function. I'll run it, and I get the expected output in the console. I think this type of module augmentation is a nice way to extend third party code that you may not be responsible for maintaining. So definitely keep it in mind the next time you're using a library that is missing a function or two you wish it had. Let's now go talk about type guards.
Demo: Using typeof Type Guards
In this demo, I'll show you how to create and use typeof type guards. The first type guard I want to demonstrate is a typeof type guard. To do that, I'm going to write a very simple function named logVisitor. It takes one parameter which may be either a number or a string. I'm going to use typeof type guards to check the actual type of the parameter passed to the function and take slightly different actions based on that. The type guard I'm going to use really consists of using the typeof operator inside an if statement. I'm comparing the type of the parameter to the string number. If that test is true, then I'll print the value as part of a log statement inside the if block. Otherwise, the parameter must be a string and I'll log it in the else block with a different message. The whole point of type guards is that they allow the compiler to perform additional type checks on your code. This allows you to get better code completion support in your editor and it allows the compiler to prevent you from using types incorrectly. Notice that if I hover over the parameter name in the if statement, I can see that the type of the variable is the union of number and string. That's because that is how the type of the parameter was declared. However, after the type guard is executed, the compiler knows more about the specific type of the variable. If it's a number, the code inside this if block will execute. If I hover over the variable inside the block, you can see that the type has been narrowed in that code path to only a number. Similarly, if it drops into the else block, it must not be a number, so hovering over the variable inside that branch reports that it's been narrowed to a string since that is the only other possible type for the parameter. Since it must be a string inside the else block, my editor treats it as a string and gives me code completion support based on that. I can just type a dot after the variable name and get a list of all of the string methods I could call on it. I'll call the toUpperCase method so that it's logged in all uppercase. I'll quickly call the function with two different values so you can see the different output. I'll call it once with a number and a second time with a string. I'll run it, and you can see the type guard did it's job and we got the two different log statements in the console. typeof type guards are great when you're working with primitive types, but you can't use them with more complex types. Okay, I'll comment out these lines while we take a look at using instanceof type guards with classes in the app.
Demo: Using instanceof Type Guards
In this demo, I'll show you how to create instanceof types guards in the Library Manager app. I'm going to go over to the classes.ts file and make sure we have a couple of good example classes to work with. I'll first add a new method to the Universitylibrarian class I've already got. I'll name it assistFaculty and it'll just output a simple message. I'll then come down a little and paste in a new class named PublicLibrarian. Notice that just like the UniversityLibrarian class, the PublicLibrarian class implements the Librarian interface. In addition to the properties and methods on that interface, the class also includes an additional method specific to this class named teachCommunity. So what I now have is two classes in this module that both implement the Librarian interface but that also each have a method that is unique to the class, the assistFaculty method on UniversityLibrarian and the teachCommunity method on PublicLibrarian. I'll now go back to app.ts so we can experiment with type guards for these classes. The first thing I'll do is jump up to the top and import the PublicLibrarian class from the classes module. I'll then come back down to the bottom and write some code. I'll declare a new variable that can store any object that implements the Librarian interface. I'll now paste in two instanceof type guards that will check the specific type of the object in the lib variable. instanceof type guards test the type against the constructor function. You place the instance you want to test on the left of the instanceof operator and the constructor function you want to compare it to on the right of the operator. If the instance has the constructor function in its prototype chain, then the test returns true. TypeScript treats this as a type guard and will then narrow the type to the specific type you tested for. Notice that if I hover over the lib variable inside the if statement, it has the same Librarian type I gave it when I created the variable. However, inside the if block, TypeScript has narrowed the type to UniversityLibrarian. That allows me to call methods on the variable that only exist on UniversityLibrarian instances like the assistFaculty method. The same thing is happening in the second type guard. I'm testing the lib instance to see if the PublicLibrarian constructor is in its prototype chain. If it is, then the type of the variable will be narrowed to a PublicLibrarian inside the if block. That allows me to call the teachCommunity method which only exists on PublicLibrarian instances. Before I run this code, I need to assign an actual instance of one of the classes to the variable. I'll assign it a new PublicLibrarian instance. If I now run it, you can see that the type guard narrow the type of the variable from Librarian to PublicLibrarian and then the teachCommunity method was called inside the if block. Okay, let's now see how to create a custom type guard.
Demo: Creating and Using Custom Type Guards
In this demo, I'll show you how to create your own custom type guards. I'll first comment out all of the previous code just so we're not distracted by it in the output. Custom type guards are handy when you need to test types that you simply can't test with typeof or instanceof type guards. Interfaces are a good example of types you can't test with either of those techniques. They're not primitive types you can test with a typeof type guard and they don't have constructor functions you can test with an instanceof type guard. The custom type guard I'm going to write will test that the object passed to it is a Book, which, you may recall, is defined as an interface. I'll name the type guard function isBook. It will accept one parameter which will be the instance to be tested. I'll specify that the function should only accept types that are either a Book or a Magazine. The syntax for the function return value is what makes this a custom type guard. For this function, it will be text is Book. text is the name of the function parameter and Book is the type I'm testing for. Inside the function, I need to return a Boolean. True, if I determine the parameter is a Book, and false otherwise. I'm going to keep this test simple. I'll use a type assertion so that the parameter is treated as a Book, and then I'll check for the existence of an author property on it. If the author property is not undefined, then it must be a Book. I'll now write a bit of code to use it. I'll declare a new variable named readingMaterial that can contain a Book or a Magazine. I'll paste in some code that uses the new type guard. The if statement calls the isBook function and passes it the readingMaterial variable. If it returns true, then TypeScript will narrow the type of the variable inside the if block to a Book. If it returns false, then the type will be narrowed to a Magazine inside the else block since that's the only other type the variable can be. You can see evidence of this if I hover over the variable in each code path. In the if statement, it's still defined as the union of Book and Magazine. Inside the if block, it's defined as a Book, and inside the else block, it's defined as a Magazine. This also allows me to use properties specific to those types inside those blocks. So that I can quickly run this code, I'm going to call the GetAllBooks function and assign the first element of the array it returns to the readingmaterial variable. I'll run the code, and you can see that the type guard did correctly determine that the variable contained a book and logged the correct message to the console.
Demo: Experimenting with Symbols
In this demo, I'll show you how to create symbols and use them as computed properties and as a mechanism for customizing how the instanceof operator works. Before I start writing any code that uses symbols, I need to change the output target being used by the TypeScript compiler. Symbols are a new feature in ES2015, so I can't use them in TypeScript that I'm attempting to compile down to ES5. I defined my compiler options in a tsconfig.json file inside my project. Inside the file, you can see that the target is currently set to ES5. For this demo, I'm going to change that to ES2015. Keep in mind that if you compile your code to ES2015, it may not all work in every browser. The browser vendors are rapidly adapting ES2015 features, but as I record this, they all still have some gaps in their implementations. Okay, I'll now go back over to app.ts. I'll create a new symbol and assign it to a variable. You create new symbols by calling the symbol function. If I pause here for just a second, you can see the code completion support I'm getting for that function. Notice that you can optionally pass it a description that can either be a string or a number. The description doesn't affect how the symbol is created and is only there for debugging purposes. I'll create my first new symbol with the description first_symbol. One important bit of syntax I want to point out here is that I simply called the symbol function. I did not treat it as a constructor and used the new keyword with it. In fact, as you can see, you'll get an error if you try to do that. Just to show you that the description passed to the function doesn't affect or determine the symbol that's created, I'm going to create another one with the same description. I'll now log whether or not the two symbols are the same. I also want to make the point that symbols are a new primitive data type. I'll do that by using the typeof operator and logging the type of one of my new symbols. I'll run the app, and you could see in the console that the two symbols are not equal even though they have the same description. You can also see that the type of a symbol isn't string, number, or object as you might expect, but it's the new primitive type symbol. With those basics out of the way, let's now look at a couple of practical applications of symbols. Because they're constants, symbols make good property keys. You may have used strings for this in the past, but if your target environment supports symbols, I would recommend beginning to use them instead of strings. To quickly demonstrate how this works, I'll create a new object literal and use one of the symbols I created earlier as a property key on the object. I just surround the variable containing the symbol with square brackets and then assign it a value like you would with any other property on an object literal. I can then retrieve the value by adding the symbol again inside square brackets after the name of the object containing the property. I'll run this, and it correctly logs the value I stored in the object. I now want to show you how you can use a symbol as a computed property key. I'll open the classes.ts file. I'm going to create and export a new symbol from the module. I'll call it CLASS_INFO and make it all uppercase to remind me that it's a constant. I'll now come down inside the UniversityLibrarian class and create a new computed property using a symbol. I'm going to make it a method that just logs some basic information about the class. I put the symbol name inside square brackets in place of a method name, but it otherwise looks like any other method on the class. It has parentheses to indicate it's a method as well as a return type. Obviously, I didn't have to use a symbol to write a method like this, but I want to show you how to do it. Implementing a technique like this guarantees that the property or method you create with the symbol will be unique. And that can be important if you're concerned with avoiding naming collisions. Let's now go back to app.ts and see how to call this method. The first thing I need to do in this file is import the CLASS_INFO constant containing the symbol. I'll now go back to the end of the file and create a new UniversityLibrarian instance. In order to call the method defined with the symbol, you access the method using the symbol as a key. The symbol is placed in square brackets. Since the class member, in this case, is a method, I add a pair of parentheses at the end to execute that method. I'll quickly run it, and you can see that the method did execute and output the correct message. Okay, the last symbol example I want to show you is how you can use symbols to customize certain internal language behaviors. I've already shown you in this module how the instanceof operator can be used as a type guard when it checks to see if a specified constructor function is in the prototype chain for an object. It turns out that the instanceof operator is represented by one of the so-called well-known symbols. Well-known symbols are built-in symbols that represent a handful of internal language behaviors. I'm going to go back to the UniversityLibrarian class and use one of the well-known symbols to effectively override the way the class responds to the instanceof operator. To do this, I need to create a new static method in the class and use the well-known symbol as the method name. The well-known symbol used for the instanceof operator is named hasInstance and it's referenced as a property on the symbol object. I create that name in square brackets just like I did the CLASS_INFO method above. The method will be passed an object which will be the object being tested to see if it's an instance of a UniversityLibrarian. The method needs to return a Boolean. It should return true if the object passed in is an instance, and false otherwise. You can perform whatever test you need to determine that inside the body of the method. In this example, I'm going to see if the object has a name property and an assistCustomer method. If so, the method will return true, which means the instanceof operator will return true. Let's now go see this in action. I'll go back to the app.ts file. I'll create a new object literal named libraryCustomer and just give it a name property. I'll now use the instanceof operator to see if that object is an instance of UniversityLibrarian. If so, I'll log a message to that effect. Otherwise, I'll log that the object does not contain a librarian. Before I can run this code with Node, I need to make sure I pass it an additional command line argument. I'm using Visual Studio Code, and its executing Node for me based on the configuration in this filename launch.json. If you're not using VS Code, you can use the same argument I'm about to show you when you run it from the command line. I'm executing all of this code with Node version 6.2.2. In order to get that version of Node to recognize my use of the hasInstance symbol, I have to pass it the --harmony-instanceof argument. With that last bit in place, I'm ready to run this code. You can see that the result is that the object is not a librarian. That's because my test said it had to have a name property and an assistCustomer property. The object I'm using here only has a name property. I'll now change that and use an arrow function to give it a very simple implementation of a method named assistCustomer. I'll run it one more time, and this time, you can see the object was found to be a university librarian. Using symbols to override functionality like that is admittedly a feature you may only rarely have an occasion to use. But you now have that tool in your tool belt and can pull it out when you need it.
In this module, we looked at some of the more advanced ways we can work with types in TypeScript. I first explained polymorphic this types and how you can use them to create clean, readable, fluent APIs. I then explained the idea behind declaration merging and showed you how the technique can be used to augment modules you may not maintain. Next, we looked at type guards. They're one of my favorite features. I really like how they narrow the type of a variable inside a block. This allows of better code completion support and compile time type checking which means fewer errors when your code gets deployed. I wrapped up with a look at symbols and I showed you a few practical ways to use this new ES2015 feature that's now supported in TypeScript. You likely won't have a need to use many of these advanced type features on a daily basis, but before you can ever solve a problem with one of them, you have to know they exist as a possible solution. You now know what's possible and will be ready to implement them when the time comes. In the next module, I'm going to focus on decorators and how you can use them to add functionality to lots of different TypeScript constructs.
Creating and Using Decorators
Introduction and Overview
Hi, this is Brice Wilson. In this module, I'm going to cover decorators. They're chunks of functionality you can add to classes, methods and other constructs with a very simple syntax and they play an important role in several large client side frameworks like Angular2. Lots of languages have a similar feature so if you've ever used annotations in Java or attributes in C#, then you'll feel right at home with decorators in TypeScript. I'll start this module off by first answering a couple of questions. What are decorators and how are they implemented? I just gave you a little preview of what they are but I'll give you some more background and go over some use cases as well. I'll also cover the syntax required to both define your own decorators as well as the syntax you'll use when applying them to declarations throughout your code. There are several different types of decorators and I'll touch on all of them and point out some of the slight differences in how they're implemented.
What Are Decorators?
Decorator Syntax and Factory Functions
Decorators are implemented as TypeScript functions. Here I've got the shell of a function I can use as a TypeScript decorator. I've named it uielement and it will be a class decorator. Class decorators take a single parameter that is the constructor function for the class. I've named the parameter target and given it the type Function. Below this, I'll add another one that I'll use as a method decorator. I've named it deprecated and it would just log a message warning developers that the method it decorates may be removed soon. The functions used as method decorators have a different signature than those used for class decorators. As you can see, this one takes three parameters. I've used very unhelpful parameter names just to save a little space on the slide. In any case, the first parameter is either the constructor function for a static method or the prototype for the class if it decorates an instance member. The second parameter is the name of the decorated member. And the third is the property descriptor for the member. Actually applying these functions as decorators is pretty simple. You just use the at symbol followed by the function name just before the declaration of the item you want to decorate. Here I've got a class named ContactForm and I'm applying the uielement decorator to it. Think of it as a stylish hat for your class in this case, decorative but still quite functional. The class' constructor function will automatically be passed as the parameter to the uielement function. So there's no need for me to specify any parameters as part of the decorator. I can apply the deprecated decorator to a method in the class like this. I just used the at symbol again followed by the name of the function. Just like the class decorator, all of the parameters for the underlying function will be passed in automatically by TypeScript. Let's now talk about decorator factories. They give you the option of specifying additional parameters when the decorator is applied. I'm going to re-implement the uielement decorator from the previous slide as a decorator factory. It's similar to the decorator functions we've already seen. You define a function and give it the name you want to use for your decorator. However in this case, the function can accept a set of parameters that will be specified when the decorator is applied to different constructs. In this example, the uielement decorator will accept a single string parameter named element. Inside this factory function, we need to return a function that has the signature of the specific decorator type it will be used with. This will be a class decorator so the factory function needs to return a function that accepts one parameter that will be the constructor function for the class. The benefit of this approach is that you can use the parameter specified when the decorator is applied inside the actual decorator function returned from the factory. Here I'm logging the parameter named element that was passed to the factory function. I can use this by applying the decorator to a class and passing a string to it much like you would any other function call. In this example, the string SimpleContactForm will be output as part of the console.log statement above.
Class decorators are likely the most common type of decorator you will encounter and use. If you were to dig around in the TypeScript source code, you would find that the type definition for our class decorator looks like this. It starts with a type parameter represented here by TFunction, that will hold the type of the class' constructor function. The constructor function will be passed as a parameter to the decorator. You can see here that it uses the TFunction type parameter for that. There are two ways you can use class decorators and that's really represented by the two possible return types in the function signature here. The first option is to replace the existing constructor function. The TFunction type parameter represents the type of the constructor passed to the decorator and it also represents one possible type returned from the decorator. If the decorator function returns a value, it must match the signature of the class' constructor function since that returned value will then become the new constructor for the class. However, class decorators aren't required to replace the constructor. If the function returns void, then the original constructor will remain in place and the logic inside the function will execute like any other function. In the next two demos, I'll show you an example of each type of class decorator.
Demo: Creating and Using Class Decorators
In this demo, I'll show you how to create and use class decorators in TypeScript. Before I can start using decorators in my app, I need to enable a special TypeScript compiler option. I've configured the TypeScript compiler for this project in a tsconfig.json file. I've got it open here. All I need to do is go to the end of the compiler option section and include the experimentalDecorators option and set its value to true. With that in place, I'm going to create a new file named decorators.ts where I'll keep the functions that define my decorators. The first decorator I'm going to create will be a class decorator named sealed. I'll apply it to classes when I want to prevent new properties from being added to it. Before I write the body of the function, I want to explicitly point out the signature I'm using. You can tell it will be a class decorator because it takes a single parameter that's a function object. Class decorators are passed to class' constructor function as a parameter. Also notice that I've declared the function return type to be void. What this means for class decorators is that the class' constructor function will not be replaced by the decorator. I'll show you another example shortly that does replace the constructor. I'll use a code snippet to paste in the body of the function. I log that the constructor is being sealed so we can see evidence in the console that it's working. I then use the built-in seal function on Object to seal the constructor that was passed in as well as its prototype. I'm now ready to apply it to classes in the app. I'll open classes.ts. Before I can use it in this module, I need to import the function from the decorators module I just created. I can now scroll down a bit and apply it to the UniversityLibrarian class. It sits at the top of the class like a hat. I just type the at symbol followed by the name of the function. Since I didn't implement the decorator as a factory with custom parameters, there's nothing else I need to add here. The class constructor parameter expected by the sealed function will automatically be passed by TypeScript. To see this in action, I'll go over to app.ts and create a new UniversityLibrarian instance. I removed the code from the previous course module just to keep it cleaner, but it's all available in the download materials. All I'm going to do right now is create a new variable named lib1 and assign it a new UniversityLibrarian instance. I'll start to build task that compiles the code and then press F5 to run it. You can see the log statement from the decorator in the console so I know I applied it correctly. However, I'd like for the log message to include something specific to the class that was decorated. To do that, I need to convert my decorator function to a decorator factory and pass in a new parameter. I'll go back to decorators.ts and make that change. The decorator will still be named sealed but I wanted to take a parameter named name. I'll now copy the original function and paste it inside this new function. The outer function is the factory function and it needs to return the inner function. I'll first fix the indentation and then rather than export the inner function, I'm going to return it. I also don't need the function name on the inner function so I'll remove that. I now want to use my name parameter in the return function that actually becomes the decorator. I'll just add it to the end of the console.log statement. Notice that the signature of the inner function hasn't change. It will still automatically be passed the constructor function for the decorated class. However, since this is now a factory decorator, I'm responsible for passing the parameter expected by the factory function. I do that when I apply the decorator to a class. I'll go back to classes.ts and make that change. You can see that I now have the red squiggly under the decorator letting me know something is not quite right. That's because it's now expecting to be passed a string. I do that just like I would pass a parameter to any other function. I'll add a pair of parentheses and a string literal in this case. I'm going to pass it the class name being decorated. I could do the same thing when I apply this decorator to other classes or use whatever string I want. I'll run the app again and this time you can see the additional string was added to the log statement in the console. In the next clip, I'll show you how to create a decorator that replaces the class' constructor function.
Demo: Class Decorators That Replace Constructors
In this demo, I'm going to create another class decorator but this time I'll show you how to return a new constructor function for the decorated class. I'm going to create the new decorator in the decorators.ts file. I'm going to name it logger. Since it will return a new constructor, I'm going to give the function a generic type parameter that represents the type of that constructor. I'm going to name it TFunction and it will extend the Function type. Remember that in the first decorator I created above, the class constructor was automatically passed to the decorator and its type was Function. The type parameter on the logger function is an extension of that same type and will be used in the function to represent the type of the replacement constructor. The first place I'm going to use the TFunction type is for the parameter passed to the decorator. This signature will still work for the constructor automatically passed to the decorator since TFunction is an extension of Function. Since I intend to replace the constructor function, I'll declare that this function will return a TFunction. This is another difference from the sealed decorator above. I didn't replace the constructor in it so its return value was just void. I'll use a code snippet in the body to declare a function object that will become the new constructor. Since this is just a simple logger decorator, I'll have it log that a new instance is being created and then I'll log the parameter that was passed in. The effect of that is just that we'll get to see the name of the class in the console. Since this new function object will become the new constructor, I need to assign it the prototype of the constructor passed to the decorator. I'll do that by creating a new object and assigning it the prototype of the new function. I'll then assign the parameter itself to the constructor property of that prototype. The last thing I need to do is return the newConstructor function. I'll use a type assertion to effectively cast the function object to the TFunction object required for class decorators. The last thing I want to point out here is why I had to explicitly declare the newConstructor variable to have the type Function. If I remove that type annotation and allow TypeScript to infer the type, you can see that the type changes. It's no longer a function object, it's a very specific function type. It's a function that takes no parameters and returns void. That generates an error below when I try to return that variable as a TFunction. I'll hover over it, and the message lets us know that the function type and TFunction are not assignable to each other. I'll put the type annotation back on the declaration. I'm allowed to cast the function object to TFunction because my generic parameter declared that TFunction is an extension to Function. Let's go write some code to use this new decorator. I'll go back to classes.ts. I first need to add the logger function to the list of imports. I'll then add the decorator to the UniversityLibrarian class. I'll also come down a little further and add it to the PublicLibrarian class. I'll go over app.ts and create a new PublicLibrarian instance just below the UniversityLibrarian instance so we can see how the decorator applies to different classes. I'll run it and you can see a pair of log statements in the console for each class. Since I logged the constructor parameter, the first set of statement shows the UniversityLibrarian class and the second shows the PublicLibrarian class. Let's now go talk about the other types of decorators you can create with TypeScript.
Property and Parameter Decorators
I'm going to go through these additional decorator types pretty quickly since the way you create and use them is very similar to the class decorators I've already shown you. I don't want to bore you with too much repetitive information so I'll focus on the small differences between them. Here, I have an example of a function I could use as a property decorator. The only real difference between a function used as a property decorator and one used as a class decorator is the function signature. This property decorator function takes two parameters. The first will be the constructor function of the class that contains the decorated member if the member is defined as static. Otherwise, it would be the prototype for the class containing the member. The second parameter is just a string that specifies the name of the decorated property. You apply decorators to a property the same way you apply them to a class. You just use the at symbol, followed by the name of the function that defines the decorator just before the member you want to decorate. Parameter decorators are nearly identical to property decorators. The first two parameters to the decorator function are the same. The first one is either a constructor function or the class prototype and the second one is that name of the function the parameter belongs to. Parameter decorator functions do have a third parameter that's different. It's the index of the decorated parameter in the list of all parameters for that function. The first parameter will have an index of zero, the second will be one, and so forth.
Property Descriptors and Method Decorators
Demo: Creating and Using Method Decorators
In this demo, I'll show you how to create and use a method decorator and how to use its property descriptor to control how the method behaves. I'm in the decorators.ts file I created earlier in this module. I'm going to add my new method decorator at the end of the file. I'm going to name is readonly and use it to decorate methods I want to make sure don't get changed programmatically after they're declared. The signature for method decorators is pretty long so I'm going to put each of the function parameters on a different line just to make it a little easier for you to read. Because I'm going to use this function as a method decorator, it takes three parameters. I've named the first one target, and it will be the constructor function for the class if the decorated method is static. Otherwise, it will be the prototype for the class. The second parameter will be the name of the decorated method, and the last parameter will be the property descriptor for the method. I'm going to use the property descriptor to make the method read-only. The first thing I'm going to do in the body of the method is just log the decorated method name and specify that it's being set to read-only. I'll then just set the writable property of the descriptor to false to prevent it from being changed later. I'm now ready to apply it to a method on one of my classes. I'll jump over to the classes.ts file. The first thing I need to do here is import the new function from the decorators module. Just to keep the console a little cleaner during this demo, I'm going to comment out the class decorators I applied to the UniversityLibrarian and PublicLibrarian classes in the last demo. I'm now going to apply the new readonly decorator to the assistFaculty method on the UniversityLibrarian class above. Because it's not a factory decorator with custom parameters, I don't need to add anything beyond the at symbol and the name of the decorator function. All of the parameters the function needs will automatically be passed in by TypeScript. Before I run this code, I'll quickly go back over to app.ts. You can see that I still have code that creates a new UniversityLibrarian instance. That's sufficient for us to see that the new readonly decorator gets applied to a method on the class. I don't actually have to call the decorated method. I'll press F5 to run the code. As expected, I get a message in the console letting me know that the assistFaculty method has been set to read-only. That all works fine but I think it would be nice if I made that decorator a little more general purpose so I can use it to explicitly make a method either read-only or writable. To do that, I'm going to rewrite it as a factory decorator. I'll go back to the decorators.ts file. I'm going to name the new decorator factory writable and have it take a single Boolean parameter that specifies if the decorated method should be writable or not. The implementation will be similar to the readonly decorator above so I'm going to cut it and paste it inside the new function. I need to clean it up a little bit. I'll fix the indentation and instead of exporting the inner function, I want to return it. I'll also remove the inner function name. Since this decorator is more general purpose and won't always make a method read-only, I'm going to remove the part of the log statement that reports that's what's happening. The last change I need to make to the inner function is to use the parameter passed to the factory function to set whether or not the decorated method is writable. I'll set the writable property of the property descriptor to the isWritable parameter. I'm now ready to use it. I'll go back to classes.ts. I need to go up to the top of the file and fix the import statement. I no longer have a function named readonly, so I'll change that to be the new writable function. I'll now come down to the assistFaculty method and change it to be decorated with the writable decorator. I'm going to pass true as the parameter to explicitly declare that the method may be changed. I'll come down just a little on this file and apply the decorator again to the teachCommunity method on the PublicLibrarian class. This time, I'll pass false as the parameter which should make the method read-only. I'll go back over to app.ts and write a little code to show you the effect this decorator has on those methods. If I try to change a method that's read-only an exception is going to be thrown. So I'm going to wrap this code inside a try-catch block. I'll use a code snippet to paste in a couple of lines that attempt to redefine the two methods I decorated. I'm just attempting to assign very simple error functions to the instance methods on the lib1 and lib2 instances I created above. In the catch block, I'll just log the error message that gets generated. Below all of these, I'll attempt to call both methods. Based on what they output, we'll know for sure if they were redefined or not. I'll now run the code and have a look at the output. The first two statements in the console are from the decorator functions. The next line is actually the error message that was generated when I tried to redefine the teachCommunity method. Remember that I passed false to the writable decorator on that method. This message tells me that I cannot assign to the read-only property teachCommunity. The last two lines in the console are the output that was generated when I called both methods. You can see that calling the assistFaculty method generated the replacement message I used in the redefine method but the call to teachCommunity still output the message in its original definition since it was not actually redefined.
Implementing Asynchronous Patterns
Introduction and Overview
Why Asynchronous Code Matters
Demo: Using Callbacks with Asynchronous Code
What Are Promises?
The Promise constructor takes a single function as a parameter and that function accepts two parameters. Here I've got the shell of a function named doAsyncWork that I could pass to the Promise constructor. The purpose of the function is to perform some asynchronous work and report the success or failure of that work using the two parameters passed to the function. Those parameters are named resolve and reject, and are functions the Promise object understands. If the result of the asynchronous work is successful, you report that by calling the resolve function and passing it any data you want to make available to the code that will process the results. Similarly, if something goes wrong, you call the reject function and pass it the reason for the failure. This is a very abbreviated example, I'll show you a more complete example in the next demo. In order to use this function with a Promise, I just pass it as a parameter to the Promise constructor. Here I'm just creating a new Promise and assigning it to a variable named p. The other thing I want to point out about this declaration is the type parameter I'm using when I declare the type of the variable. The variable will be a Promise and the type parameter, a string in this case, is the type of data that will be returned if the Promise is resolved successfully. In this short example, I created a separate function declaration for the doAsyncWork function I'm passing to the Promise constructor. I did that for the sake of clarity, I think what you'll see more often and probably choose to do yourself is to pass an arrow function to the Promise constructor like this. This accomplishes the same thing as the example above. The function I'm passing in has two parameters named resolve and reject, and inside the function I call those functions as needed depending on the results of the asynchronous work that was performed. Let's now see how to process the results once the Promise has either been resolved or rejected. Handling the results returned via Promises is primarily done with two instance methods named then and catch. In this example I'm calling a method that returns a Promise. If the Promise resolves successfully, it will return a string. I've specified string as the type parameter on the variable declaration. I can now use that Promise object to register the functions that should be called when the asynchronous code succeeds or fails. I call the then function to specify the function that should be called when the Promise is resolved successfully. I'm passing it an arrow function here that accepts one parameter. The string data parameter will automatically receive the data passed to the resolve method we saw on the previous slide. Here I'm just logging the string that gets passed in. The then function also returns a Promise, so to handle errors I can just chain it to a call to the catch function. I'm also passing it an arrow function that will execute if the Promise is rejected or if an exception is thrown in the then function above. The function passed to catch takes a single parameter that I've called reason. Similar to the string data parameter above, this parameter will automatically receive the value passed to the reject function we saw in the last slide. Let's now go see Promises in action in a demo.
Demo: Creating and Using Promises
Demo: Writing Asynchronous Code with async/await
In this demo I'll convert the code from the previous demo that used Promises exclusively to a different implementation that takes advantage of the newer async and await keywords. I'm going to start this demo like I did the last one and make a copy of the previous code. I'll then comment it out, so you'll have it in the course download materials but I'll paste it down below so you can see how I'm going to modify it to use async and await. The first thing I'm going to do is delete all of the code that initiates the asynchronous work between my two console.log statements here at the bottom. Now I'm going to write a new async function that will call the getBooksByCategory function above. Since it will be an async function, the declaration begins with the async keyword. I'll name the function logSearchResults and it will take the category I want to search for as a parameter. I'll declare a new variable named foundBooks to store the array of titles returned from a call to getBooksByCategory. Since that function returns a Promise, I can use the await keyword in front of the function call to effectively pause execution inside this function until the Promise is resolved. I'm not going to change the getBooksByCategory function above, so it will still simulate a network call by waiting two seconds to return the found book titles. Once that happens, the code here will continue executing and I'll just have it log the titles to the console. I now just need to add a call to logSearchResults between my two log statements below. Just as we saw in the two earlier demos, we should see both of these log statements appear before the results of the search are shown. If that happens then we'll know we weren't blocking other work from happening while the long running call ran. I'll run the app, wait a couple of seconds, and we do get the expected results after both log statements. Let me now show you a couple of options you have for handling errors using this technique. One option is to wrap the code in the async function inside a try catch block. I'll use a code snippet to quickly show you what that might look like. I'm not doing anything fancy, I've still got the same two lines of code, they're just now wrapped in a try block and I can handle any errors that occur in the catch block. That would work fine but I'm going to undo that change and show you another option. Notice that if I hover over the logSearchResults declaration, we can see that it actually returns a Promise. All functions declared with the async keyword return Promises. If I had used a return statement inside the function body to return some value, you would see the type of that value listed as the type parameter for the Promise. Since I didn't return a value inside the function, this one just uses void for the type parameter. Since async functions return Promises, you can use the Promise methods we saw in the last demo to unwrap any values or errors they contain. I'll demonstrate that by chaining a call to catch onto the call to logSearchResults. It will be passed a reason if the Promise is rejected just like we saw in the last demo. I'll use a simple arrow function to log that reason. So that we can see this in action, I'll change the category I'm searching for to biography since I know I don't have any of those and run the code again. As expected, the reason is successfully logged to the console. I really like the more linear coding style that using the async and await keywords enables. I think the result is much more readable code that still includes all of the asynchronous benefits you get from the other techniques we've seen.
Writing Cleaner Code with TSLint
Introduction and Overview
Hi, this is Brice Wilson. In this final module of the course, I'm going to show you how to use a utility named TSLint to help you write code with fewer errors that also conforms to a set of code styling standards you can configure. I'll start off this module with a brief explanation of TSLint and how you can use it as part of your TypeScript development process. I'll then quickly get into a demo and show you how to install TSLint, run it, and configure the linting rules it uses to alert you to problems. In a second demo, I'll show you how to install and use a really great extension for Visual Studio Code that will alert you to linting issues as you're writing your code.
What is TSLint?
Demo: Installing and Using TSLint
In this demo, I'll show you how to install and run TSLint as well as how to perform some simple tweaks to its default set of rules. I'm going to start out here on the TSLint website so you can see where to go to get more information about it. You can see the URL here in my browser. Everything on the site is pretty self-explanatory, I think. Notice at the top right of the page there are a few links. I specifically want to highlight the Rules link. Clicking on it takes you to a page that describes all of the rules that come with TSLint. They're presented on this page in different sections, and each rule name is a link that will take you to a page explaining exactly what the rule does and any options that are available for it. You'll definitely want to keep this page handy as you're configuring how you want each rule to work on your project. Back on the homepage, there's also a section showing you how to install TSLint with npm. It shows you how to install it locally for a specific project as well as globally if you want it available anywhere on your machine. I'll now jump over to a terminal window and run that command to install it on my machine. I just type npm install tslint. I'll also install typescript at the same time so that I have the latest version, and I'll use the -g option to install them both globally. That only takes a couple of seconds to run. Once it's done, I can run tslint from the command line. It includes several different command line arguments. To get a quick look at those, I'll first run it with the --help option. I'll pipe the output to the more command so the first part of the output doesn't scroll past. Here you can see some of the more common options you might use with the command. The --config option which can be abbreviated -c lets you specify the location of the tslint configuration file. And the --init option lets you create that configuration file if you're setting up a project with tslint for the first time. That's the option I need to use now with the library manager project. There's detailed help about all of these options further down in this output, but I'm going to exit out of this command, clear the screen, and create a new config file for my project. I'll just type tslint --init. I'll go back over to Visual Studio Code, and you can see that I have a new file added to the project. The default set of rules for tslint are stored in the tslint.json file. We'll take a closer look at some of the rules in just a second. First, I want to run tslint to see what kinds of things it finds. Back in my terminal, I'll type tslint followed by the name of the file I want it to analyze. I'll just have it look at app.ts. I could also use wildcards here to analyze more than one file at a time if I wanted to. I'll press Enter and all of the output is printed to my screen. Each line of the output represents one rule that's being violated. They start with the name of the file in the position in the file where you can find the problem. There's also a brief message telling you what's wrong. I'll scroll up just a little and you can see I've got several different errors here, but a couple of them are repeated over and over. It's obviously expecting new lines to be indented with spaces and reporting lots of problems since I am using Tabs in my code. A little further down are several instances where it's reporting that a single quote should be a double quote. Since those make up the majority of the problems, I'll focus on them first. I'm generally in the habit of indenting lines with Tabs, not spaces, and I normally quote strings with single quotes rather than double quotes. I want those to be the standards for this project, so I need to edit the default values that come with TSLint. I'll go back to Visual Studio Code and find those rules in the tslint.json file. Before I make those rule changes, I want to quickly point out how this file is organized. It begins with a rules section at the top. Each key in that section is the name of a different rule. Each key has a value that configures that rule. Some rules have options and some don't. If the rule doesn't have any options, the value is simply a Boolean specifying if the rule is enabled or not. For instance, the no-eval rule doesn't have any options, but it is currently enabled. Rules that do have options will have a json array for the value. The first element in the array is a Boolean specifying if the rule is enabled. The remaining elements set different options for the rule. You can find all of the available options on the TSLint website I showed you earlier. The indent rule here is one of the rules that does have a configurable option. Since the first element is true, I know the rule is currently enabled. I can also see that it's currently configured to expect indentation to be done with spaces. That's one of the reasons I got so many errors earlier. I'll change that to Tabs for my project. I'll scroll down just a bit and find the quotemark rule. It's enabled and set to double. Since I use single quotes, I'll change that value to single. I'll go back to my terminal and see if tweaking those rules got rid of some of my errors. I'll clear the screen and run tslint again. Okay, that looks much better. These remaining problems probably need to be fixed in my code. I've got a comment that needs to start with a space on line seven, a missing semicolon on line 13, and some missing whitespace on line 15. I'll now go try to find and fix those problems. I'll open up app.ts and then close the sidebar to give myself a little more room. The first problem was a missing space before the comment on line seven. You can see that this line doesn't have a space after the two forward slashes like the comment above it. I'll just add a space in there. Next was a missing semicolon on line 13. I'll fix that by adding one to the end of the line. The last error reported missing whitespace on line 15. That's referring to the lack of a space between the if keyword and the opening parenthesis around the condition. Adding a space there should clear that up. I'll now go back to my terminal and see if that fixed everything. I'll run the same command one more time. This time there was no output, so all of the code passed. That's the goal. In the next demo, I'll show you how to use an extension in Visual Studio Code to make this workflow a little smoother.
Demo: Using TSLint with Visual Studio Code
In this demo, I'll show you how to install and use the Visual Studio Code extension for TSLint. The first thing I need to do is find and install the TSLint extension. I can open the extensions view by clicking this bottom button on the left side of the window. It immediately shows me lots of popular extensions. I'm just going to type tslint in the search box at the top. It quickly finds the extension I'm looking for. This is a really great extension that I highly recommend you try out if you're using Visual Studio Code. It was developed by Erich Gamma, and you can see that it has many thousands of downloads and a five-star rating. Installing it only takes a second. I'll click the green install button, and once it's done, I'll click the blue Enable button. I'm told here that in order to enable it I need to restart Visual Studio Code. I'll click OK to go ahead and do that. That also only takes a second. I'm now ready to try it out. You can tell it was enabled by the appearance of the little TSLint button in the bottom right corner of the screen. I'll click on that, and it opens an output window that informs me the linter is running. I'll now purposely type a couple of mistakes so we can see it in action. I'll add a new console.log statement and leave off the semicolon at the end of the line. It may be a little hard to see, but I get a small green squiggly line at the end which alerts me to a problem. I'll hover over it, and a description of the rule I violated pops up, missing semicolon. I'll put my cursor at that position, and a little lightbulb appears next to it. I'll click on the lightbulb and the tslint extension offers to fix the problem for me. I'll click on that message and it inserts the semicolon for me at the end of the line. I love being able to find out about and fix problems as soon as I create them. That's much better than finding them later as part of a build step. I'll show you one more quick example. I'm going to type another console.log statement, but this time I'm going to indent the line with spaces. Again, you can see I've got a green squiggly line where TSLint found the error. I'll hover over this one and I'm told tab indentation expected. I'll delete the leading spaces and replace them with a Tab, and the error goes away. I know this was a really quick demo to show you some of the basic features, but the extension does lots of other cool tricks too. If you're using Visual Studio Code, definitely try it out. I think you'll like it.
I'm in favor of using any tool that seamlessly integrates into my workflow and helps me write better code. TSLint is definitely such a tool for TypeScript developers. In this module, I showed you how to install TSLint, edit some of its default linting rules, and how to integrate TSLint right into Visual Studio Code with an extension you can download right inside the editor. Okay, that wraps up this module as well as the course. I hope you've enjoyed it and learned lots of new things about TypeScript you can immediately apply in your own projects. Thanks for watching!
Brice has been a professional developer for over 20 years and loves to experiment with new tools and technologies. Web development and native iOS apps currently occupy most of his time.
Released15 Nov 2016