What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
A Practical Start with TypeScript
by Roland Guijt
This course uses a demo-first approach to get you familiar with TypeScript. You will cover all of the main language features of TypeScript by building software for a vending machine serving drinks and candy.
Start CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Recommended
Course Overview
Course Overview
Hi everyone, my name is Roland Guijt, and welcome to my course, A Practical Start with TypeScript. I'm an independent software developer and trainer based in the Netherlands. Writing browser code or Node.js code in TypeScript gives you many advantages compared to writing JavaScript. It saves you from error-prone code, and because type checking is done as you code, it'll save you lots of time because errors will become apparent before you run your app. You'll learn everything to get started with this powerful language in this course. I'm going to cover everything you need to know by writing a vending machine simulation. Some of the major topics that we'll cover include setting up a programming environment, language features, applying object orientation, and code structuring. By the end of this course, you'll be ready to write a TypeScript app from scratch. Before beginning the course, you should be familiar with the basics of programming. JavaScript knowledge is handy, but not really needed. I hope you'll join me on this journey to learn this great alternative to JavaScript at Pluralsight.
Preparing to Write an Application
Course and Module Introduction
Hi, and welcome to the first module. The focus in this course is on getting you started with TypeScript as soon as possible. A great way to learn is to actually write an application, so we start with that after a brief intro. You can program along or just watch me do it. I will provide you with explanations while I code where they are needed. This course consists of four modules. The module you're watching now is about getting prepared to program in TypeScript. In the second module, we'll look at the TypeScript language basics, and after that, we add object orientation to that. And in the final module, you'll see how to further structure your code. This course is for beginners. The only thing I assume is that you know the basics of programming, that you have some idea of what a variable is, a function, and common data types like a Boolean and string, for example. Experience with JavaScript will help, but it is not really needed. The course will evolve around an application which I very much enjoyed writing. It's a vending machine simulation. Here's what's in this module. First I'm going to explain what the advantages are of TypeScript, and what it is exactly. Then, we'll look at a TypeScript compiler and how to install it. You'll also see that there are many editors with which you can create a TypeScript app. I'm using Visual Studio Code in this course, so I'll show you how to install and configure that. Next, we'll look at the options the TypeScript compiler, or tsc, offers. One of the options is to generate source maps. You will learn why they help with debugging. Referencing other TypeScript files is a good idea, and we'll see why that is. But, no time to waste, let's start by looking at the benefits of TypeScript versus JavaScript in the next clip.
TypeScript vs. JavaScript
Maybe you've used JavaScript before. If so, this example will make clear why using TypeScript instead of JavaScript could be an advantage. Take a look at a simple piece of perfectly legal JavaScript. Apparently there's nothing wrong with it. The code editor doesn't detect any errors. In reality, this is bad code, this is bug sensitive. Let's rename the .js file to a .ts file, which is the TypeScript extension. It becomes immediately apparent that something is wrong. TypeScript doesn't allow the declaration without a var keyword. It's a bad practice because the variable will be present in the global namespace. Let's put var before it, but there's more going on. I first assigned a string to the output variable, and later in the function, I assigned index to it; also a very bad bug sensitive practice in JavaScript. TypeScript just doesn't allow it. In TypeScript I can assign a type to a variable when I declare it. I can, for example, say that input should be a string. As soon as I do that, the call to the function supplying an array isn't valid anymore. It's again a bad idea to misuse the input parameter to accept multiple types. This example gives you a pretty good idea why using TypeScript could be an advantage over using JavaScript.
What Is TypeScript?
TypeScript is an open source project maintained by Microsoft, and it transpiles into a JavaScript version of your choosing. The syntax of TypeScript is not coming out of Microsoft's hat. It is following the ES6 syntax as much as possible. For features in TypeScript not present in the ES6 standard, it is even using the ES7 and beyond specification. TypeScript is called TypeScript because it adds typing. When you declare a variable, you can specify that it is a string, for example, and that is something JavaScript doesn't support. So what are the benefits of having a typed language versus a non-typed language like JavaScript? If a variable is of the type string, for example, you can't accidentally put a number in it later. VS Code, or whatever tool you use, will probably detect this while you type, and if not, it will be detected by tsc when you try to transpile. This fact will also decrease the likelihood of bugs in your code, and it gives the tooling you use the ability to help you autocomplete a function call to an object, for example, speeding up the code writing process. With a non-typed language, you have to do endless type checking. A function, for example, can have a parameter. In the function you assume the parameter will be a number, but in fact it could be anything, a function, an object, a string, etc. To prevent bugs, you will have to check if the thing you are receiving is really a number. Just by typing the parameter, this becomes unnecessary. So the bottom line is, it'll save you a lot of time.
The Transpiler
The compiler for TypeScript is called tsc, TypeScript Compiler abbreviated. This compiler is implemented cross-platform. It can run on Mac, Windows, or Linux. Tsc is a transpiler; it transforms the TypeScript we're going to learn in this course to JavaScript. That's because the platform you code against, for example Node.js or a browser, doesn't understand TypeScript. I will write code for a browser in this course, but with the skills gained in this course, you could just as easily write code for other JavaScript platforms, such as Node.js. The easiest way to install tsc is by installing it as a Node.js package with the Node Package Manager, or npm, at the command line. But before you do that, make sure you have Node.js. If you don't, download it at nodejs.org for your operating system and install it. On Windows, when downloading Node.js, you run the Windows Installer. Be sure to include the runtime, npm, and add everything to the path. On other platforms, the installation experience is different, but make sure you are installing with these options. Now open a command prompt or terminal window and enter npm -g typescript. This will download and install typescript, and a -g flag indicates that want typescript installed globally and not just in the directory you are in right now. When I did this for the first time, I struggled a bit with executing tsc, but it was fine when I rebooted the OS because the new path is then correctly loaded. Let's try tsc. Create a new file called test.ts with copy con. Now create an empty TypeScript class like this; the curly braces mark the beginning and the end of the class. Now at the command prompt type tsc test.ts. When tsc is done, you can see there is another file in the directly called test.js. You can see that tsc translated the TypeScript into JavaScript, ECMAScript 5 to be exact. Building and translating TypeScript by hand is probably not what you want in a bigger project. Syntax highlighting and intelligence would be great. I'll talk about the different editors in the next clip.
The Editor
You are not tied to using a specific editor for TypeScript development. Visual Studio makes sense if you're already using it to develop ASP.NET server-side applications, for example, and the latest version also supports Node.js. It is a full-blown integrated development environment, or IDE. You can do a lot of different projects with it. It is free for small businesses or individuals, and it offers a great developer experience, but the downsides are that it takes a lot of resources, takes a long time to start, and runs on Windows only. If you install Visual Studio, tsc is automatically installed as well. A cross-platform, more lightweight commercial IDE alternative is WebStorm, which is entirely focused on creating web projects. Sublime Text and Atom are editors for all kinds of text out of the box, but can be enhanced to code editors with the right plugins. For the demos in this course, I picked Visual Studio Code, VS Code abbreviated, which is a code editor that is free and open source, fairly lightweight, runs cross-platform, and it supports and recognizes all kinds of code files, including TypeScript out of the box. Let's take a look at how to set up VS Code in the next clip.
Setting up Visual Studio Code
Download and install Visual Studio Code on code.visualstudio.com. On that site there are also instructions how to install it for your operating system. In all cases, it is very easy to do. When you're done installing, create a directory somewhere on your drive that will hold the project. I call the directory vendingmachine. Now start Visual Studio Code by typing code vendingmachine. Were you're not in the command line, you can also just start it and go to File, Open Folder, and browse for the new directory. By the way, I switched the color theme to Light for increased readability of this recording. The default theme is Dark. The Explorer view on the left shows the contents of the directory, which is empty for now. You can switch the Explorer on and off by clicking on the top icon on the left of the screen. The magnifying glass icon lets you search the contents of files in the open folder. The icon below that is for the Git integration. And the final icon is to debug your project in Visual Studio Code. Out of the box, VS Code supports debugging for Node.js applications, but with plugins you can also debug against a particular browser. My experience when doing browser projects is that the debugging in the browser itself is a great experience, which I will show you shortly. So we're not going to use the debug icon in this course. If you want a deeper understanding of all things VS Code, there is a course dedicated to it by John Papa in the Pluralsight library, but you don't need the info to continue watching this course. So let's create a new TypeScript file by hovering over the directory name and clicking the first icon. Let's name it helloworld.ts. You can see VS Code understands that this must be a TypeScript file, and it will provide for syntax highlighting and intelligence automatically. Let's create an empty class again called HelloWorld this time. In the next clip you'll learn how to transpile it in VS Code using a build task.
Configuring a Build Task
To transpile the HelloWorld TypeScript file with tsc within the editor, we have to run a build task. You can run it by pressing Ctrl+Shift+P or Cmd+Shift+P on a Mac, which brings up a Search box with which you can search for VS Code commands. Type in build, and you can see it finds the Run Build Task command. That has the shortcut, Ctrl+Shift+B, which I will use from now on. If I run the task, VS Code reports that no tasks runner is configured yet. Luckily, you can configure it by just clicking on Configure Task Runner. After selecting that, we get a list of possible tasks, and we need a TypeScript task, of course. This creates a tasks.json file in a newly created directory called .vscode. This file defines what should happen if the build task runs. This template provides a task for tsc, but there are many more templates of tasks to work with. The template specifies the command to run is tsc, and this is something that has to be executed in the Shell, meaning the command prompt or terminal window. It is also configured to only send output to the Output pane if there are errors. The Output pane can be toggled by going to View, Toggle Output, or by pressing Ctrl+Shift+U. The next line is the args setting. It lists the command line arguments tsc should receive. Let's just specify helloworld.ts as an argument, so that that file will be transpiled. Finally, a problemMatcher looks at the output the shell command generates that can be reported to VS Code as errors. The $tsc is a problem matcher that conveniently ships with VS Code. At this point, save the file and run the task by pressing Ctrl+Shift+B or Cmd+Shift+B. You can see that the task runs because of the spinner in the lower left corner. The problem matcher will report problems in the Output window, so it's a good thing to keep an eye on that. When it's finished, you can see the .js file is generated. It's ready to be referenced in the HTML app we're going to build. However, I would like a task configuration that is slightly more sophisticated. Let's take a look at that next.
tsconfig.json and tsc Command Line Arguments
Because we are going to add multiple TypeScript files and add HTML and CSS, I will restructure the project a bit. First, let's delete the .ts and .js file we created earlier. I create a folder called ts for all TypeScript files. You can easily create a new folder in VS Code by pressing this second icon when you hover over the project name, which is the directory name the project is in. I also add a js folder for the tsc output. What I don't like is having to modify the task each time I add a .ts file to the project. I want tsc to ultimately transpile all files in the ts folder. Let's configure that in the tasks.json file. I specified -p, short for project directory, as a command line argument followed by the directory where the TypeScript files are. In that directory, I create a file called tsconfig.json. When tsc finds a file named like that in the project directory, it will read it and configure itself further. There are many things you can configure here, like the inclusion or exclusion of files and many compiler options of which we're going to use a few. In json construct compiler options, I specify that the target format for tsc is es5, which stands for ECMAScript 5. So what options do you have here? ES5 is a JavaScript version fully supported in all current browsers, and also by Node.js. ES6, also known as ES2015, which is its official name, is the upcoming new standard. Eventually, all browsers will natively support this, and Node.js will too. Finally, ES3 is there for browsers that don't support ES5. The second compiler option I set is specifying an outFile followed by the path and file name. If an outFile is specified, TypeScript will not make a JavaScript file for each TypeScript file it finds. Instead, it will create one JavaScript file with a TypeScript for all TypeScript files combined. This way I only have to reference one JavaScript file in my HTML, which will also speed up the process of loading up my web page, because only one web request is needed to fetch all the JavaScript. If you don't like this behavior, you can set the outDir option instead with a path where you want the individual JavaScript files. The final option we will use is a sourceMap option. This instructs tsc to create a source map file for each JavaScript file it generates. Let's see what these guys are in the next clip.
Source Maps
In a moment, we're going to set up an HTML file to load the transpiled JavaScript. I can, of course, press F12 in my browser to debug the generated JavaScript, but I don't want that. I want to debug the TypeScript code I wrote, a source map file helps. On the left of this slide you see some TypeScript I've written, and on the right the transpiled ES5 JavaScript. You can see matching the code using line numbers won't help. ES5 tends to be lengthier than TypeScript. Source maps map the transpiled code to the TypeScript code, and the good news is that all current browsers support source maps.
Bootstrapping an Application
Let's add the first real class for the vending machine project now. Add a file called vendingMachine.ts in the ts directory with a class called VendingMachine, which is empty for now. I now create a second TypeScript file called bootstrapper. The bootstrapper will take care of starting up the application. The idea is that the bootstrapper TypeScript file is the entry point for our application. It will instantiate classes, and in the case of the vending machine application, set up data binding. It's no problem if you don't understand data binding for now; we will come to that shortly. For now, the only thing I'm going to do in the bootstrapper is create a new object of the class type, VendingMachine. Again, I will show you the inner workings of classes later in the course. After pressing Ctrl+Shift+B, you can see the JavaScript file is created, as well as a source map file. When I quickly open up the JavaScript file, you can see that the source map file is referenced. The browser will pick this line up. Now let's add an HTML file to the project. I tend to use the CSS the bootstrap open source project provides to make my application look nice, so I add that in the head. In the body, I create a div with a bootstrap class container, which will create a div with the right margins for its content. I just put an h1 in now with the text, Vending machine simulation. As the final line in the body, I add a reference to the JavaScript file. Remember, it contains the transpiled contents of the two TypeScript files combined. Now I want to load this HTML file in the browser. A handy extension for VS Code to facilitate this is called View In Browser. You can install it by pressing Ctrl+Shift+P and choose Install Extension, and then type view in browser, then press the first little icon on the extension style to install it and restart VS Code. You can now just press Ctrl+F1 to load up the HTML file in the browser. I'm using Chrome here. When I open up the F12 Developer Tools in Chrome, I can see there's something wrong. I get the error, VendingMachine is not a constructor. Let's find out what's wrong in the next clip.
Debugging
When I go to the Sources tab, I can see that thanks to the source maps, I have the .ts files available in the browser as well, even though we didn't directly reference them. When I open the bootstrapper.ts file, I can see that my app went south as soon as it was executed, but why? Let's set a breakpoint at this line and also one in front of the vendingMachine class and press F5 to refresh the browser. The line of code in the bootstrapper gets hit first. It won't work because the class isn't loaded yet at this point. Let's view the transpiled JavaScript, and you can see that the instantiation of the class if first, and then the class gets defined, so tsc is putting the transpiled JavaScript in the app.js, but in the wrong order. Let's see how I can resolve that in the next clip.
Referencing Other TypeScript Files
Somehow I have to let tsc know that the bootstrapper.ts file depends on the vendingMachine file, that way it will understand that it has to put it in the JavaScript for the vendingMachine class first. I can do that by using a triple slash reference in the bootstrapper TypeScript file. When I type ref in VS Code, I can see there is a code snippet available for it. I just have to press Tab, and the reference line is inserted, all I have to do is type vendingMachine.ts between the quotes. When I press Ctrl+Shift+B again to execute the build task and look at the transpiled JavaScript, you can see it's okay now, and when I refresh the browser, the error is gone, so we're good to go. Referencing other TypeScript files not only helps when you have one big JavaScript file with all it transpiled to TypeScript. It also helps with IntelliSense, in this case in VS Code.
Summary
In this module you learned the components of a development environment and how to set them up. You saw how to create the project structure for your application and how to bootstrap and debug it. After watching this module, you should have a good idea of what TypeScript is and how to set it up. In the next module, we'll dive into the syntax of TypeScript.
Understanding the Language Basics
Module Introduction
This module is about getting to know the TypeScript language basics. After watching this module, you will have a good understanding of the TypeScript syntax, and you can apply built-in, as well as create custom types. At the end of the module, we will have a working vending machine that is able to accept quarters, and it can get you a can of soda.
Classes
You saw me create empty classes in the previous module, but what is a class? A class is like a blueprint. Once we have a blueprint of a house, for example, you can build as many actual houses as you want by using the blueprint over and over again. Translated to code, the blueprint is the class, and the actual house is an object. In JavaScript before version 2015 there were just objects, which you could define as a literal or with a constructor function. In TypeScript there are actual classes. You can define data in a class with something called a field. For example, you can specify that a house class should contain a doors field of the type number. Now, when you create objects using that class, each house object can determine its own number of doors. An object of a class is also known as an instance of that class. Besides data in the form of fields, classes can also contain functionality, which are functions that are called methods when they are defined within a class. So a house class could define functionality to open doors. As a result, all objects can call this method. In the next clip, let's look what data types there are in TypeScript.
Data Types
There are two categories of data types in TypeScript. The first one is the built-in types. A Boolean can contain the value true or false only, a string is a sequence of characters surrounded by quotes, and a number is any number; it doesn't matter if it has decimals or not, or is negative or positive. The second category is the data types that are created by you, the developer. You will see all of these in action very soon, and I will explain them when we get to them. The last one on the list is the class. You already saw me using that type. In TypeScript, every variable is assigned to a type, but sometimes you want to opt out of that. In JavaScript, you can assign any type to every variable you declare. This is a practice TypeScript wants to protect you against because it leads to error-prone code. You can implicitly opt out of the typing system of TypeScript by declaring a variable of the any type. This will give you the JavaScript behavior back. You should use this as less as possible, because it turns off the biggest plus of using TypeScript, but sometimes you need it to, for example, make your code compatible with the JavaScript library. In the next clip, we are going to implement a coin class for our vending machine.
Implementing a Class
I added an image directory to the project and put an image of a quarter and an image of a soda can in it by just copying it over to that folder. Now let's create a new file in the ts directory called coin.ts, and create a class called Quarter. The process of remodeling a real-world object, like a quarter into a class is called abstraction in object orientation. Let's give this class a field of the type number and an initial value of 0.25, which can be written just as .25. Semicolons are optional in TypeScript, so it's up to you if you want them behind every line of code or not. There's a naming convention going on here. Most developers write the names of types in Pascal casing, that means every new word in a name starts with a capital, and the names of members, like a field, are written using Camel casing; it starts with a lowercase letter and every new word in the name starts with a capital. Now I add a method called getImageUrl, which accepts no parameter, because there's nothing between the parentheses, and it should return a string in this case. You can also define methods, which don't return anything. In that case, the return type is void. The actual functionality of the method is between curly braces. I just returned a string, which is the relative path to our coin image. There are a couple of things to explain at this point. In the next clip, I'll start with type inference.
Type Inference
Once a field is of a certain type, you can't put a value in it of another type. To demonstrate, I will try to put a string in the field value in the getImageUrl method. Every time you want to refer to something that is on the class level, use the this keyword followed by a dot, and in this case, the field name. When I try to put a string in the value field, you can see VS Code detects an error telling me a string is not assignable to a number. Now let's do something interesting. Let me remove the type declaration of the number field. You can see I'm still getting the same error. Even though I didn't specify it explicitly, TypeScript knows values should be a number, because I assigned a number to it. This is known as type inference. You can apply the same trick to a method. When you leave off the string return type, the return type of the method will still be strongly tied to a string, simply because of the fact that I'm returning one. In the next clip, learn about how to do class encapsulation with TypeScript. Let me try something out here. I create a variable called coin and assign it
Access Modifiers
Let me try something out here. I create a variable called coin and assign it to a new instance of the Quarter class. Note that this variable has Quarter as its type because of type inference. When I type in the variable name followed by a dot, you can see I can access the value and getImageUrl members of the class. I can also change the value in the value field, but in this case I don't want that. I want a quarter to always have the value .25, and I don't want something outside the class to modify the value. In TypeScript you can control accessibility of members by typing access modifiers in front of them. If you don't do that, like me in this class, the access modifier is public implicitly; that means something outside of the class can read a value and write a value. Let's make the value field private by typing in the private keyword in front of it. Controlling access to a class like that is part of the encapsulation principle in object orientation. As you can see, I can't access value from outside the class, but I can from within the class, but this is not something I want for the Quarter class. I like to be able to read a value, but not modify it outside the class. Let's see how we can do that in the next clip.
Properties
To make sure the value field can only be read, we create a property. Properties also support encapsulation in object orientation; they control access to the class. I write the so-called accessor, which can be get or set. In this case I only use get because I want the value to be read-only. Note that the getter is a function called Value with a capital V. It returns the number inside the private field value. So now I have limited access to the value field by using the property. There is no difference in syntax between reading a field and reading a property from outside the class. The compiler will also agree if I put a number in the Value property, even though there is no setter, but the value won't actually be set. Should you want a setter, you can add it in like this. It's a function again, but now it accepts a parameter, which is the value put in the property. You could do some validation of the value first before you put it in the private, if you like. That's in fact the whole point of properties; you can control what goes in and out. Next, we're making a start with the vendingMachine class.
Functions and Arrows
Now that we have a coin class, let's work on the vendingMachine class so that it can accept a quarter. People can insert multiple coins in the machine, so we have to keep track of a total. I create that first as a paid field in the class with a starting value of 0. We're only going to use it internally, so we make it private. Next, we add a function, also known as a method to the class called acceptCoin. It accepts an object as a parameter of the type Quarter, the class we just created. I assign the incoming parameter to a variable called coin. The object can be accessed in the function by that name. It's also possible to have the function accept multiple parameters; you just separate them with commas. Next, I specify a return type of the method. This method won't return anything, so I make it void, and then the curly braces in which I will code the actual functionality of the method shortly. It must increase the value of the paid field with the value of the coin, but first let me introduce you to a different way of writing a function. The same function can be written differently by using an Arrow function. Arrow functions in other languages are sometimes known as lambdas. The difference in syntax is that there is an = character between the name of the function and the parameter list, and there's an arrow behind the return type. Again, because of type inference, specifying the return type is optional. If the implementation of the function only consists of one line, you don't have to use curly braces, but you can if you have to. If you want to use multiple lines, you still need to. There's also an important difference that has to do with the this keyword. In JavaScript and TypeScript by default, this, in a function refers to the object that is calling the function. That can be confusing because what this is can vary. It is especially confusing if you're used to languages like C#. In arrow functions, this always refers to the object that is directly outside of the function scope. That means that if the function is in the vendingMachine class, this will be the object of that class. Just like in JavaScript, it is also possible to put functions directly in a variable. When you do that outside any scope, such as a class, this will point to the JavaScript window object. I want to make sure that this in the code I'm going to write references to the vending machine object, so I refactor that acceptCoin method to an arrow function, then all I'm going to do is add the value of the coin to the paid number field. Let's design the part of the UI that is going to accept coins in the next clip.
Connecting the UI
So the next step is to create a button in the HTML that will simulate that we're inserting a quarter. I also want the total paid amount on the screen. For styling and positioning, I'm using Bootstrap, but I also want to add some of my own styling in a file called site.css. I add this file to the project and paste in all the styles we're going to need for the demo. I've already added a div that is going to be the container for the UI. Container is a bootstrap class. In the container, I add a div, which will cover two-thirds of the width of the screen, and another one that will take the remaining one-third. In Bootstrap, 100% width is divided into 12 columns. In the right div, taking one-third of the screen, I want the paid functionality. I add a Paid label with a span that is going to contain the actual amount paid. It has the id total so we can easily reference it in TypeScript, which I will do in a moment. Next, I add the button with the Bootstrap classes btn and btn-primary, which has an onclick event calling the acceptCoin function with a new Quarter. Let's switch to the vendingMachine. In the acceptCoin method, I need additional code that updates the span with the id total. I search for the element in the HTML, and I capture that element in a variable. Then I update the innerHTML, which is the text it contains to the new total. Let's run the app and see if it works, and it does, but I'm not very happy with the way this works. I will explain why in the next clip.
Connecting the UI with Knockout.js
I don't like littering my classes with code that involves the UI. To solve that, I'm going to use a data-binding library called Knockout.js. Knockout is not the only solution to this; you could do it with Angular and other JavaScript frameworks. What I like about Knockout is that it does the data binding only, so I can focus on TypeScript without having to explain a complicated framework. Knockout uses the MVVM principle. MVVM stands for Model View ViewModel. A UI and a JavaScript object called the view model are bound to each other. In this example, we have a Day property in the TypeScript object that is true now, and you can see the view reflects this. When I change the property in the object to False, the view will respond. So this is model to view, and when it becomes Day in the view, the property in the view model will update its value automatically. This is the view to model part. So the data binding between the object and the view is two way. Let's see how this works for the demo and set up Knockout for this project. First, I've created a lib directory under the js directory in our project, and I copied over the Knockout JavaScript library. Next, I can add a reference to it from the HTML file. Make sure it loads before the transpiled TypeScript code runs. Next, I switch to the code of the bootstrapper class. I want to configure Knockout to use the VendingMachine object as the view model. I can do this by calling applyBindings on the ko object, which is defined in the Knockout JavaScript file. As you can see, TypeScript is not happy with me using the ko object. That is because the typing system wants to know what kind of object ko is. It has to know its definition. Let's explore type definitions in the next clip.
Type Definitions
You already saw me using several JavaScript objects, such as document. They're, of course, not written in TypeScript, but they seem to have the typing in place. Documents select element by ID only accepts a string as a parameter, and code completion in VS Code works perfectly. The reason for that is that TypeScript already contains type definitions for standard JavaScript objects. Type definitions act like a strongly-typed interface in front of the actual JavaScript object you're trying to access. Of course, TypeScript doesn't know the typings for objects in external libraries, such as Knockout.js. Luckily we can add the type definitions where needed. A good place to get type definitions is definitely type.org. There's a huge list of definitions available for all kinds of libraries. I downloaded the definition file for Knockout, and I created a subdirectory on the ts where it is sitting now. You can recognize definition files by the d in the file name. Referencing type definition files in your code with a triple slash ref directive isn't needed because tsc automatically finds and uses them as long as they are in the project directory structure. Once I have the file in place, I can call applyBindings on the ko object and supply the VendingMachine instance. Let's refactor our code to use Knockout in the next clip.
Refactoring the UI Logic
Instead of a number, I want the paid property to be a ko.observable with an initial value of 0. This is an object that supports the two-way data binding I was talking about. In the acceptCoin function, I now get rid of the UI logic and define a variable that is called oldTotal, and it captures the current value that is in the observable. You can retrieve the value by just calling the object like a function. Then, I want to give paid a new value. You can do that by, again, calling the observable as a function and just pass in the new value. In this case, the old value added to the coin value. Now switch to the HTML. In the span for the total, I now add a data-bind attribute to set up a binding. This is an attribute Knockout looks for. I say that the text of this element should contain the value of paid. When I now run the app, you can see it works like before, but I now got a much better separation of concerns. I'll talk about the let keyword next.
Let
In the vendingMachine class, you saw me use the let instead of the var keyword for the old value of paid. It doesn't change anything at first glance, but it has its advantages. Let's consider this code. I have the acceptCoin function in a slightly different version here. I first read the value of paid, let's say it's 2, and then in the if block, I'm checking if the value is greater than 10. If that's the case, I define another variable called much with the value true. When using the var keyword, both JavaScript and TypeScript use function scope; that means everything you declare in a function will be available in the function no matter where you declare it. In the background, in fact, the JavaScript interpreter will even put the declaration of all variables in the first lines of the function. That is called hoisting, so when I do the console.log later in the same function, I can get to both values. When I change the var keywords to let, I'm getting block scope. Now for the variable I declare within the if block, a block by the way is delimited with the curly braces, the scope of the variable is that block only, so when I try to get to the value outside the block it's declaring, it won't work. Using let is a best practice. Why would you declare a variable in a block when it's hoisted anyway? You will learn about erase in the next clip.
Arrays
When we created the Quarter class, we also gave it an image URL. Let's put that to use by displaying the actual coin in the UI to click on instead of the button. For now we just have one coin, but we're going to build more in the next module. So let's add a collection of coins to the vendingMachine class called acceptedCoins. Since we have only the Quarter class right now, I'll make this a Quarter array. As you can see, you can just put two brackets behind the type name to declare an array. All elements of this TypeScript array must be of the Quarter type; you can't put anything else in. I can give it an initial value right away by using brackets behind the equal sign. If you want to add more items after the declaration, you can use the push method on the array. It has more methods to read and manipulate its contents. When transpiled, this array will, of course, be a JavaScript array, so the methods you can call in TypeScript are the same methods you use in JavaScript. Right now it doesn't really make much sense to use a Quarter array with one quarter in it to determine the accepted coins. But hang on, we will refactor this in the next module when we add more coin types. Let's get rid of the button in the HTML and add a div, this time with a foreach binding, which will iterate through the array. As the value, I specified a new acceptedCoins array. Note that the array is not observable. We don't need that here because we're going to read the array only once at startup, so the UI doesn't have to change automatically when the array changes or vice versa. The contents of this foreach div is rendered for each item in the array. Since I want the coin's image to be rendered, I can just specify an img tag with an attribute binding. I tell Knockout to get the src attribute of the img tag by calling getImageUrl on the coin and the alt attribute from the value field. Now something needs to happen when a user clicks on the coin, so I write a second binding. I tell Knockout whenever the coin is clicked, call the acceptCoin method. Since the context in the h4 is the coin, I have to explicitly say the this method should be called on the parent, which is the vending machine object. Knockout will automatically pass the coin object in as a parameter. Now let's run again. I can now click on the actual coin image to add to the total amount paid. In the next clip, I'm going to add some more classes and talk about constructors.
Constructors
A vending machine has cells. Each cell has a certain stock of a certain product. For the demo, I don't want each individual product to have a separate image. I want to place the products in categories, so let's add the classes to support this. I'll start with a productCategory class. I first make a new file called productCategory.ts, and add a class to it called SodaCategory, which contains a category name and an image URL. I've already added the image to the img directory. Next, I create a file called product.ts, which for now just contains one product called CocaCola. It has a name, a price, and a category, which is a new instance of SodaCategory. Don't forget to reference the file containing the categories. Then I'm adding a cell class to the vendingMachine TypeScript file. A TypeScript file can contain multiple classes. When instantiating a cell object, the cell should be tied to a product. With a constructor, I can make the passing in of, in this case the product, compulsory while the cell gets instantiated. Since we have only one product for now, I will accept an object of the CocaCola type. Putting public in front of the parameter means that I want to implicitly create a class-level property with the same name for the value that is passed in. The Cell object also has to contain a certain stock of the product, and an indicator that something is sold, which will be used to trigger the CSS animation. Both are Knockout observables. In the next clip we'll take a look at static members in a class.
Static Members
I want to put the logic to get products in a separate class. Let's create productFactory.ts. It contains a class called productFactory with one static method, GetProduct, which will, for now, just return a new CocaCola instance. In the next module, I will augment the class to return a random product, but you could also create logic to fetch products from a server, for instance. A static is a field, property, or method, in other words a member on the class level. Normal members are meant for the object instances of the class. When you define something as static, it is available on the class level, and it won't be available at the object level. So if I define a static doors property in the house class, you can get to that value by accessing the class and not the object. In the case of productFactory, I want to be able to call GetProduct without having to instantiate an instance of the class. Let's put the productFactory to work in the next clip.
Enums
The VendingMachine has to be flexible in terms of number of cells supported. I want to support 6, 9, or 12 cells, or small, medium, and large. I create a new type in the vendingMachine.ts file that supports this in the form of an enum called VendingMachineSize. VendingMachineSize is an enum. By declaring an enum, I'm declaring a new type. So I can declare variables or accept method parameters and type them to the new enum. I will construct a property for the vending machine shortly called size, which will be of the type VendingMachineSize. Instead of assigning the number of cells to it directly, I can tell it to use the medium size, which corresponds to a machine with 9 cells. But what are the benefits of using enums? First, it can act as a constraint. We could type the size property as a number, but in this case I don't want a machine size of 7 or 10; I only want 6, 9, and 12 as options. By creating the enum and making the size property use the enum type, anyone attempting to set it will only have these options. Secondly, it can increase the maintainability of the code. Let's say I want this changed to a number of cells for a certain size. For example, large is becoming 15 instead of 12. Using an enum, I only have to change it in one place, in the enum itself. Lastly, code can become more readable when using enums because of the semantics of enums. Suppose the size property would accept numbers, a size of 6 isn't really semantic, 6 what? Setting the size of medium is more friendly to the user of the class.
Loops
Let's add a cells field to the vending machine. It's type is a Knockout observable array. An observable array is the same thing as an observable, it just has methods to add or remove elements from the internal array it uses. The initial array of items is empty. Now I'll implement a size property. I want it to be a write only property, so I can only use the setter from outside the class. It should use the new enum as its type. Even though you assign values to a property using the equal sign, in the background a function is executed with a value on the right side of the equal sign passed in as a parameter, which I call givenSize. First, I'll clear the cell's observable array to make sure it's empty by assigning an empty array to it. Then, I'm constructing a for loop by using the code snippet in Visual Studio Code. Just type for and press Tab. The for loop declares a variable called index and assigns it the value of 0. Let's make the var a let for this one. The next part is an expression. The loop will continue to run until the condition is false, and the final part is what the loop should do after each iteration. In this case, increase the index variable by 1. In the loop, I declare a product variable and asked the productFactory to get a product for it. Remember, I can call the GetProduct method directly on the class without a need to instantiate productFactory, because it's a static method. Next, I add a new cell to the cells array by constructing a new cell, passing in the product by using its constructor. In the bootstrapper code, I set the size of the VendingMachine to medium, and that way the cells array will get filled with 9 cells. Let's go to the HTML. The first two-thirds of the screen will be filled with the cells. I add the CSS class machine to the col-md-8 div and add a data-bind to it. I want its contents to be rendered for each of the cells in the cells array. Each shell should take one-third of the available width, and I add the cell CSS class for styling. Within each shell, an image should be displayed taking up half of the width of the cell. I add a data-bind to the img tag contained in the div. This time it's the attr binding that is data-binding the view model class to the attributes of the tag. I want the src attributes to be bound to the image URL of the category and the alt to the name. The other half of the width is the product info. I want the product name to be displayed there, the stock of the cell, and the price of the product. Now let's run the app. You can see each of the cells are filled with a product, but I still have to add the functionality to pay for an item. Let's do that in the next clip.
Finishing Up
We should add the functionality to select a cell. For that I introduce a new field called selectedCell to the vendingMachine class, which is an observable with an initial value of a cell filled with the Coca-Cola product. I'm also adding a new method called select to define what should happen if a cell is selected. It accepts a cell as a parameter. I'll make sure that the sold observable in the cell is set to false, and then I just store the selected cell in a new selectedCell field. Back to the HTML, I want to give the cell a click binding, calling the select method on the vendingMachine class, and I add a binding called css. The selected css class should be applied if the product of the selected cell is equal to the product of the cell this HTML is for, $data is the current cell of the foreach loop. I want to add the sold CSS class if the sold observable is true. This will trigger a CSS animation contained inside the CSS. Below the coin display, there should be something to remind the user which product is selected. So let's create an h3 for that and change the context for everything in the h3 to the selectedCell with a width binding. I just displayed the product name and its price with a $. Finally, I add a button that lets the user pay for the product. It has the Bootstrap classes, btn and btn-primary, as well as the custom class, payButton. It should only be enabled when there's enough money inserted. When I click on the button, the paying should be done. So let's add a canPay field to the vending machine. It's a special observable called a pureComputed. It will be true if the paid amount plus the product price is greater or equal to 0. The pureComputed will recalculate its value automatically if one of the observables mentioned in the expression changes. Now the pay method. Let's first check if there is stock of the selected product. If not, I just display an alert and quit the further execution of the method. I declare a variable, currentPaid, which contains the amount that is paid so far. Then, I subtract the product price from currentPaid, and put that amount in paid. I have to do a little rounding trick, because JavaScript uses floating numbers by default, and that could result in rounding differences. Then, I determine the stock of the cell and subtract one from that stock. And next, I indicate that the product in the cell is sold to trigger the CSS animation. Now when I run the app, you can see we have a working vending machine.
Summary
You've reached the end of this module, and we covered a lot. We looked at using classes as building blocks and the data types they contain. You learned how typing works and what properties and functions are in classes. Type definitions help out with external JavaScript libraries, and let is a better alternative to var. Arrays are TypeScript's and JavaScript's collections, and enums can make your job as a developer much easier. In the next module, we're digging into the object orientation capabilities of TypeScript.
Applying Object Orientation
Module Introduction
In this module I'm covering some more principles of object orientation. At the end of the module you will see a vending machine operating with more coin types, product categories, and products. First, I'll explain what problem polymorphism solves, so you can recognize it in action. We look at class inheritance, which enable base classes, which can be abstract. And lastly, you'll learn about applying interface types. We will apply all these concepts to the vending machine app. Let's continue to work on our coins in the next clip.
The Problem Polymorphism Solves
Here's the coin.ts file. Let's add another coin class called Dime, which also has a getImageUrl method that returns the path to the Dime image, which I added to the img folder together with the other coin images we're going to use. In the vending machine TypeScript file, I want to add a Dime object to the acceptedCoins array, so that it will display on our HTML page. As you can see, that doesn't work. That's because the elements of the array are tied to Quarter, and a Dime is not a Quarter. The need arises here to have an array of coins in which I can stick all coin objects, such as Quarter and Dime. TypeScript supports this through a concept called polymorphism, which will you see me implement in the next few clips. To apply polymorphism we need inheritance, and that's the topic of the next clip.
Inheritance
I want a common type for all coins, something that defines all common things about a coin to the actual coin, like a Quarter, just has to implement the specifics. So let's create that so-called base class. Now I have to let the Dime and Quarter classes know that they are coins. I can do that by using the extends keyword behind the class name. Everything that is in the coin type will be available in the Dime and Quarter classes, provided it is not declared as a private. There's nothing in it right now, but we'll work on that. This is called inheritance. Coin here is the base class defining everything that is common for all coins. A Quarter and Dime are the derived classes, with inherit everything non-private from the coin. Each class can inherit from just one class, so if you apply multiple levels of inheritance, the class diagram will look like a pyramid. In the next clip, you'll see how this applies to polymorphism.
Polymorphism Implemented
When I switch to the VendingMachine class again, you can see that the line with the acceptedCoins array now seems to work if I type the array to the Coin class instead of Quarter. It works because Dime and Quarter inherit everything from Coin, and because of that, we can say dimes and quarters are coins. The compiler understands this, so it can type the array to Coins and put a Dime and Quarter in, and when I run the application, it all seems to work because Dime and Quarter have a getImageUrl method, which is called in the HTML. But let's say I forgot to implement the getImageUrl method, then the application goes south. Somehow as I code, I want to make sure each derived class has a getImageUrl method. I'll make that happen in the next clip.
Abstract Classes
Right now, having the Coin base class doesn't make much sense. It doesn't contain anything. The Coin class isn't meant to be instantiated, it is just meant to add as a base class for the actual implemented coins, like Dime and Quarter. But we do want to make sure derived classes implement the getImageUrl method. You can do this by specifying getImageUrl in the base class and omitting the method body, and marking the method as abstract, and if one member in the class is abstract, the whole class has to be marked as abstract. After doing that, the class can't be instantiated anymore. Now at compile time, the presence of a getImageUrl method is checked in derived classes. It is now compulsory to implement it. The code won't compile without it, so I quickly put it back and have the code working again. Let's move more code to the base class in the next clip.
Super
Since every coin has a value, it makes sense to move that property to the base class, as well. In this case simply because it saves you lines of code, and it increases the maintainability of the code. I also add a constructor accepting the value that stores value in the class. In the constructors of the derived classes, I can now call the constructor in the base class with the correct value. You can call the version of a constructor in the base class by using the super keyword. So by using the super keyword, you can call the base class constructor, but there's another way to use the super keyword. Let's say we have a class called Base with a method called baseMethod, and a class derived from base with the same method. By using the super keyword in the Derived class, I can call also the method with the same name in the base class. Of course, you could also pass in parameters, if needed. Because you decide on the moment you are calling the baseMethod, you can do stuff before and after the baseMethod runs. To make the code even shorter, you can omit the declaration of value in the base class, and just put an access modifier in front of the constructor parameter. TypeScript will implicitly create a property for value. Let me introduce you to a new access modifier in the next clip.
Protected
I already talked about the private access modifier, which hides members of a class for everything but the class itself. A public keyword also exists, which opens up members to other classes; it is a default access modifier. But there's another one. Protected works like private; members are available for the class itself. In addition to that, protected members are also accessible in classes that derive from the class. In this example, there's a field called num and a class called Base with the protected keyword in front of it. When I derive a class from Base, num is accessible, but when I instantiate a Derived class in an unrelated class, for example, num is not visible. Using protected just like using private is useful for encapsulation. It helps you hide complexity in classes without losing the ability for derived classes to access these members. Hiding complexity helps you to write more maintainable code, so I recommend to hide as much of a class as possible. You keep the coupling between classes low that way, and when changing the code there is little chance that other parts of the code break. In the next clip, you'll see how protected works in our advantage for the vending machine.
Using Protected Members
Now let's create two more coins, a half and a dollar, and add them to the acceptedCoins array in the vending machine. For productCategory I create a base class with a protected string containing the imgPath string and name field, and an abstract getImageUrl method. SodaCategory just fills this name property with a value and implements the getImageUrl method, which is now compulsory. Because the path string is protected now, derived classes can easily use it while at the same time I hide the field from everything not deriving from the ProductCategory base class. Now I have that in place, it's easy to create a lot of other categories, and, of course, I added the appropriate images to the img directory. Let me show you how to use interfaces in the next clip.
Interfaces
Interfaces are cell-defined types just like classes. They can't contain any implementation. They act as a contract for a class. Everything defined in an interface must be implemented by the class implementing the interface. A class can only inherit from one class, but it can implement multiple interfaces. The difference between an abstract class containing only abstract members and an interface is that an interface isn't bound to a certain class tree. Unrelated classes can implement an interface. Let's quickly make this more concrete. I create an interface called Product containing a name, price, and category property. Every class that implements this interface must contain everything that is in the interface, so in this case a name and a price property. There is an exception to the rule; if you specify a member with a ?, the presence of that member is optional. Now I apply the interface on the CocaCola class. Implementing the interface on the CocaCola class will just work, because it already contains all the members specified in the interface. When I create another class called Initial, which I will use as the product that is selected when the application is started and let it implement the interface, Visual Studio Code detects that not all necessary members are present, and it's only satisfied when I add them. You can see I can omit the category. Now let me paste in some other products that all implement the interface. Now let's refactor the product factory. Polymorphism also works with interfaces, so I can let GetProduct return an object of a class that implements the product interface. Next, I calculate a random number and depending on what the number is, I return a specific product. In the constructor for the Cell class, I also change the constructor parameter to be of type Product. Now let's change the initial product in the vending machine from CocaCola to our special Initial product and run the app. Great, we have a fully functional vending machine now.
Summary
In this module you learned how inheritance and polymorphism work together and how abstract classes form ideal base classes. The super keyword plays an important role in inheritance to call into the base class, and interfaces are a great way to use polymorphism with unrelated classes. In the next module, we're going to see how to optimize the code using additional code structure options and generics.
Structuring Code
Module Introduction
You're watching the fourth module of the Getting Started with TypeScript course. Here's what I'm going to talk about; the global namespace isn't really a feature of TypeScript, but it's important to understand how it works. You'll see how to group your code when an application gets larger with namespaces and modules. The TypeScript language feature called Generics can save you many lines of code. And finally, something that's experimental in TypeScript for now, but important to understand if you're going to work with TypeScript in combination with Angular 2: Decorators. The global namespace is up next.
The Global Namespace
Every time you declare a class in TypeScript, it is added to something called the global namespace, which is like an application-wide bucket of classes. The thing that identifies the class is a name. In an application, we declare a lot of classes, and there are no problems as long as every name is unique, but what happens if a class is added that is different but has the same name? Then the class that came in first is overwritten, and isn't available anymore. This not only goes for classes, but for everything you declare inside a global namespace. You're okay if are to declare something that is contained in another structure, like a function or a class, but all classes we've declared so far in the vending machine are in the global namespace. This isn't much of a problem in the case of our vending machine demo, because it is a small application with a limited set of classes, but when the application gets bigger and multiple persons are writing TypeScript for it, this can lead to hard-to-find bugs. When the amount of classes increases, you might want to group them using containers named in a smart way. By putting classes in some kind of container, only the name of the container itself will be exposed to the global namespace, so there's less chance of naming conflicts. From a logical point of view, this might also be a good idea. Accessing a class via its container might add some needed semantics. For example, our Quarter class could be contained in something called Coins, and then you would access Quarter by using coins.quarter. Finally, these containers add an extra layer of encapsulation. Classes within the container can either be exposed to the world outside the container, or only be accessible from within it. There are two types of these containers in TypeScript, namespaces and modules. In the next clip, you'll see namespaces in action.
Namespaces
Let's wrap all of the coin-related classes of our vending machine app in a namespace called Coins. If you want a certain type in a namespace to be available outside the namespace, you must mark it with the export keyword. I do this for all classes. When I switch to the VendingMachine class, you can see all coin classes can't be found anymore. I must prefix them with a namespace name in order to get to them. Now that I have are namespace in place, I can declare something that is only available in a namespace just by omitting the export keyword. I declare a string called imagePath, which contains the base path to the images. Now in each coin class, I can use the base path to get to the coin's image, but anything outside of the namespace can't access it. In a transpiled code, a namespace is just an extra JavaScript object that is wrapped around anything that is inside it. TypeScript also supports namespaces that exist across multiple TypeScript files, just use reference paths to all files you need. It's also possible to nest namespaces. In this example, there's a Coins namespace and a Money namespace. Note that also the Coins namespace has to be exported if you want it to be available outside the Money namespace. When you want an instance of Quarter, you have to refer to it using the two namespaces. This could lead to code errors lengthy. You can reduce it by using the import statement. With imports, you essentially create a variable that is a shortcut to the nested namespace. You can then use that variable to reference everything that is inside it. I'll talk about modules next.
Modules
Modules are another way to group classes, variables, and functions without being exposed to the global namespace. They are similar to namespaces, but this time the TypeScript file itself serves as the container, and after transpilation, of course a JavaScript file will serve as container. As soon as a TypeScript file is a module, it has to be explicitly imported in any other file in order to use everything that is inside it. When using modules, you don't use triple-slash reference paths anymore, because the import itself loads a reference to file as we'll soon see. Converting just one file to a module doesn't work; it's all or nothing. If you choose the way of the modules, you must convert all your files to modules. TypeScript relies on a module loader to load the modules. You'll need an external JavaScript library to facilitate this. The module loader will actually load the module. In case of a web application, like our vending machine, it will request it from the server when it's first used, so there's no need to reference individual JavaScript files anymore in script text. In the case of our vending machine app, there's just one page, so as soon as the application starts, all modules are requested from the server. That doesn't give us much benefits right now, but as the project becomes more complex with multiple pages or tabs, the application will feel faster to the user because the JavaScript only gets loaded when it's needed; there's no big weight up front. There are several module loaders available, just pick one you like. In the demo, I'm going to use RequireJS, which is frequently used with web projects, but if you develop for Node, you'll probably use CommonJS. Each module loader uses a certain module format. You can specify what module format tsc has to output in the tsconfig.json file. It's also possible to load modules only when needed. You could, for example, wrap the loading of a module inside an if statement. I'm going to modularize the vending machine in the next clip.
Applying Modules
Back to Visual Studio Code. I've put the require.js module loader in the js lib directory and added a type definition file for it. Now let's make some modules. This is the original coin.ts file we had. To make this file a module, all I have to do is put the export keyword in front of all the classes I want to be available outside the module. When I now switch to the VendingMachine class, you can see none of the classes in the coin.ts file are found now. I have to add an import statement if I want to make use of the types in the coin module. In this case, I'm using the * wildcard to import everything from the coin module. Notice that you reference the file without the extension. All coin types will be made available in the Coins variable in this case. Now, I can just prefix the use types with Coins, and I'm done. Because I'm now using the input statement, I can now delete the reference path to coin.ts. Next up is the projectCategory file. Instead of prefixing every type with the export keyword, I can also use a different technique. After all types are declared, I export everything I want in one go by specifying all types between curly braces. I can even rename the original type with the as keyword. In this case ChipsCategory will have the name PotatoChipsCategory outside the module. I again import everything from the module in the product file, and make it available in the categories object. Now I prefix all category types with it. Notice that I have to use a new name for ChipsCategory now. The product.ts file itself also must be converted to a module. In the vending machine, I now only want to import certain types from it. You can do that by using curly braces, and list the types you want with commas in between. These types will then directly be available. You can give the type another name by using the as keyword again. To show you that not only classes can be exported, let me refactor our productFactory. I can remove an abstraction layer by taking the GetProduct function out of the class and just make the function available. Because the function is outside the context of a class, I have to add the function keyword. Now I export that function, and I mark it as the default export. You can do this for only one item in your module. Back in the vendingMachine file, I can import the function using any name I want, and I don't have to use the curly braces because it's a default export. The vendingMachine file itself also must be converted to a module. I don't have to export Cell, because it's only used from inside the module, but I will need VendingMachineSize, and of course VendingMachine itself. Finally, I somehow have to bootstrap our newly modularized app. I can't use the bootstrapper.ts file, because we are now in module mode, so I delete it. We're going to bootstrap the app in the index.html file. I remove the app.js reference, which is not needed anymore, and add a script tag containing JavaScript code to bootstrap our app. Also don't forget to add a script tag for Require.js. I instruct require to fetch the vendingMachine module, and in the second argument, I specify a function that gets called when that is done. As an argument, I get the vendingMachine module in object form. Now in the function, I can just write the same code as I had in the bootstrapper.ts file. To support these external module loaders, you can set the output format for modules in a tsconfig.json file. Beware that the option outFile will put all modules in one file, which is not what you want when using modules; use the outDir option instead, which will generate an individual JavaScript file or TypeScript file. Let's save everything, run the build task, and then run the app. You can see everything works, and all JavaScript files are now loaded individually. In the next clip, I'll talk about generics.
Generics
Let's imagine we're in the business of creating a collection class for strings called StringCollection. It has an add method to add strings to the collection, which accepts only strings. And there will be more methods in the class working with strings; for example, to retrieve values from the collection. I'm perfectly happy with the class, but sometime later, I need the same class for numbers, so I have to add a class to my project, which is essentially the same. The only thing that is different is the type of the items it works with, and it might also become handy to have a collection class for quarters, as well, for which I have to add another class with the same structure. Generics can save you a lot of lines of code in this scenario. This is a generic class. Generic classes accept a type parameter, in this case called T, because it has T as a type parameter, it is called Collection of T. You're not tied to using T as a types parameter, but it's convention to call it T if you just have one. So T is a type, which is unknown at the time you create a class, but you can use it everywhere you want in the class. Here I'm using it to type the parameter of the add method. The type T has to be known as soon as you create an instance of the class. At that time, you fill in the type. You can see that I'm creating a collection of string here. What effectively happens is that TypeScript will create a new class in the background substituting T with string. The class isn't anywhere in your TypeScript code, but it will be present in the transpiled code. Using the same generic class, I can now create all kinds of collection classes reusing the code I wrote in a generic class over and over again. Generics are not only for classes. You can create generic interfaces and functions, as well. In the next clip, you'll see generics applied to the vending machine.
Applying Generics
Let's take another look at the Cell class. When I hover over ko.observable, you can see that we, in fact, already used generics without knowing it. Ko.observable is generic. Stock is a ko.observable of number, and sold is a ko.observable of Boolean, but I didn't specify a generic parameter. This is, again, type inference at work. Because I assigned 3 to stock, TypeScript infers that it has to use a number as the type parameter, and I assign false to the sold observable, which is inferred as a Boolean. You can of course also be explicit if you want, by adding the generic type parameter. Also, take a look at our acceptedCoins array. It's tied to Coin, so only types that derive from Coin can be in the array. This is in fact just one way to define an array in TypeScript. You can also use the generic class, Array, to create an array of Coin to achieve the same using other syntax. In the next clip, we'll level up with generic constraints.
Generic Constraints
The collection class is a good demonstration showing the power of generics, but what if you want to call functions on your T object inside the generic class, or access fields or properties? In this example, you'll see what I mean. Now I have a CoinCollection of T class, which has a function to add an item. The item is taught in a private array, and it has another function to get the total money value of all coins in a collection. Incidentally, you see another way of doing loops in TypeScript here. The forEach method accepts a function that gets executed for each item in the collection, it's ideal to use an arrow function here that adds the face value of the coin to the total variable. Well this code doesn't work. Why? Because TypeScript doesn't know what type T is. TypeScript is a typed language, and it won't allow you to read the value property because T could be anything. To solve this, you can add a generic constraint to the class. Here I state that T must be a descendant of Coin, and then Coin, the value property is present, so I'm allowed to use it. Of course, TypeScript won't accept a type for T now if it doesn't derive from Coin. You'll experience the real power of generics if you start deriving from generic classes or use generic interfaces. Here you see a new class, QuarterCollection, which derives from CoinCollection of Quarter. Now everything that is common for all coins can be in the base class CoinCollection, and in QuarterCollection I can implement specifics for quarters. Let's look at decorators in the next clip.
Decorators
Decorators are an experimental language feature in TypeScript, so the way they work might change in a future release. The reason to add the topic to the course is that Angular 2 uses them. Angular 2's preferred language is now TypeScript, and maybe you're watching this because you want to use TypeScript with Angular 2. Decorators are functions, but a special kind of them; you will see that shortly. Decorators can be used on classes, but also on methods, properties, and parameters, and the best part is that you can write a decorator once and use it on as many classes as you like. Because decorators are experimental, you must explicitly enable them in tsconfig.json first. This is a simplified example of using the component decorator in Angular 2. HomePageComponent is a normal class with a name property, and it has a Component decorator above it. Because the Component decorator is above the class, it works with that class. Here it's defining a view for the HomePageComponent class, which data binds to the name property of the class. The Component decorator itself is a function, in this case in the Angular framework, but you can write decorators yourself, as well. The function takes the object involved, the instance of the class it's above, and a configuration object as a parameter. It uses that information to do something with the object passed in. In this case, create a view for it, but it could be anything, in fact. It's possible to use decorators to do interception of classes, add extra functionality to them, and even change them.
Summary
To summarize, exposing anything to the global namespace can't be avoided, but do it as less as possible as a best practice. Namespaces and modules help with that, if you're creating larger applications. Generics can help you to reduce your code base, and decorators are there to add reusable functionality to your methods, classes, properties, or parameters. Thank you for watching this course. I hope you have a blast with programming in TypeScript.
Course author
Roland Guijt
Roland is a Microsoft MVP enjoying a constant curiosity around new techniques in software development. His focus is on all things .Net and browser technologies.
Course info
LevelBeginner
Rating
(251)
My rating
Duration1h 39m
Released18 Jul 2016
Share course