
Dedicated to my lovely wife, Jacqui Griffyth.(And also to Peanut.)

is an experienced IT professional who has held senior positions in a range of companies, most recently serving as chief technology officer and chief operating officer of a global bank. Now retired, he spends his time writing and long-distance running.
is a senior consultant and a senior analyst/developer using Microsoft technologies. He works for BluArancio ( www.bluarancio.com ). He is a Microsoft Certified Solution Developer for .NET, a Microsoft Certified Application Developer for .NET, a Microsoft Certified Professional, and a prolific author and technical reviewer. Over the past ten years, he’s written articles for Italian and international magazines and coauthored more than ten books on a variety of computer topics.
The best way to get started with TypeScript is to dive in. In this chapter, I take you through a simple development process to create an application that keeps track of to-do items. Later chapters show how TypeScript features work in detail, but a simple example will be enough to demonstrate how the basic TypeScript features work. Don’t worry if you don’t understand everything in this chapter. The idea is just to get an overall sense of how TypeScript works and how it fits into an application.
Four packages are required to get ready for this book. Perform each installation described in the following sections and run the test provided for each of them to ensure that the packages work as they should.
The output from the first command should be v14.15.4, indicating that Node is working and the correct version has been installed. The output from the second command should be 6.14.10, which indicates that NPM is working.
At the time of writing, the latest version of Git for all platforms is 2.30.0.
The TypeScript compiler is called tsc, and the output from the command in Listing 1-4 should be Version 4.2.2.
The final step is to install a programmer’s editor that supports TypeScript. Most popular editors can be used for TypeScript development, but if you don’t have a preferred editor, then download and install Visual Studio Code from https://code.visualstudio.com. Visual Studio Code is an open-source, cross-platform code editor that is free to use and is the editor I used while writing the examples for this book.

The Visual Studio Code welcome screen
Some editors will let you specify a different version of TypeScript than the one contained in the project, which can cause errors to be displayed in the code editor even when the command-line tools show successful compilation. If you are using Visual Studio Code, for example, you will see the version of TypeScript that is used displayed at the bottom right of the editor window when you edit a TypeScript file. Click the version that is shown, click Select TypeScript Version, and select the version you require.
To get started with TypeScript, I am going to build a simple to-do list application. The most common use for TypeScript is web application development, which I demonstrate for the most popular frameworks (Angular, React, and Vue) in Part 3 of this book. But for this chapter, I build a command-line application that will keep the focus on TypeScript and avoid the complexity of a web application framework.
The application will display a list of tasks, allow new tasks to be created, and allow existing tasks to be marked as complete. There will also be a filter to include already completed tasks in the list. Once the core features are in place, I will add support for storing data persistently so that changes are not lost when the application is terminated.
The npm init command creates a package.json file, which is used to keep track of the packages required by the project and also to configure the development tools.
I describe the TypeScript compiler in Chapter 5, but these settings tell the compiler that I want to use the latest version of JavaScript, that the project’s TypeScript files will be found in the src folder, that the output it produces should be placed in the dist folder, and that the commonjs standard should be used for loading code from separate files.
The file contains regular JavaScript statements that use the console object to clear the command-line window and write out a simple message, which is just enough functionality to make sure that everything is working before starting on the application features.
The TypeScript file and the JavaScript file contain the same statements because I have not yet used any TypeScript features. As the application starts to take shape, the contents of the TypeScript file will start to diverge from the JavaScript files that the compiler produces.
Do not make changes to the files in the dist folder because they will be overwritten the next time the compiler runs. In TypeScript development, changes are made to files with the ts extension, which are compiled into JavaScript files with the js extension.
Classes are templates that describe a data type. I describe classes in detail in Chapter 4, but the code in Listing 1-10 will look familiar to any programmer with knowledge of languages such as C# or Java, even if not all of the details are obvious.
This is an example of a type annotation, and it tells the TypeScript compiler that the id property can only be assigned values of the number type. As I explain Chapter 3, JavaScript has a fluid approach to types, and the biggest benefit that TypeScript provides is making data types more consistent with other programming languages while still allowing access to the normal JavaScript approach when needed.
Don’t worry if you are not familiar with the way that JavaScript handles data types. Chapters 3 and 4 provide details about the JavaScript features you need to understand to be effective with TypeScript.
Support for static data types is only part of the broader TypeScript objective of safer and more predictable JavaScript code. The concise syntax used for the constructor in Listing 1-11 allows the TodoItem class to receive parameters and use them to create instance properties in a single step, avoiding the error-prone process of defining a property and explicitly assigning it the value received by a parameter.
The change to the printDetails method removes the public access control keyword, which isn’t needed because TypeScript assumes that all methods and properties are public unless another access level is used. (The public keyword is still used in the constructor because that’s how the TypeScript compiler recognizes that the concise constructor syntax is being used, as explained in Chapter 11.)
All the statements shown in Listing 1-13 use pure JavaScript features. The import statements are used to declare dependencies on the TodoItem and TodoCollection classes, and they are part of the JavaScript modules feature, which allows code to be defined in multiple files (described in Chapter 4). Defining an array and using the new keyword to instantiate classes are also standard features, along with the calls to the console object.
The code in Listing 1-13 uses features that are recent additions to the JavaScript language. As I explain in Chapter 5, the TypeScript compiler makes it easy to use modern JavaScript features, such as the let keyword, even when they are not supported by the JavaScript runtime that will execute the code, such as older browsers. The JavaScript features that are essential to understand for effective TypeScript development are described in Chapters 3 and 4.
The generic type arguments are enclosed in angle brackets (the < and > characters), and the Map in Listing 1-18 is given generic type arguments that tell the compiler that the Map will store TodoItem objects using number values as keys. The compiler will produce an error if a statement attempts to store a different data type in the Map or use a key that isn’t a number value. Generic types are an important TypeScript feature and are described in detail in Chapter 12.
The getTodoItems method gets the objects from the Map using its values method and uses them to create an array using the JavaScript spread operator, which is three periods. The objects are processed using the filter method to select the objects that are required, using the includeComplete parameter to decide which objects are needed.
The TypeScript compiler uses the information it has been given to follow the types through each step. The generic type arguments used to create the Map tell the compiler that it contains TodoItem objects, so the compiler knows that the values method will return TodoItem objects and that this will also be the type of the objects in the array. Following this through, the compiler knows that the function passed to the filter method will be processing TodoItem objects and knows that each object will define a complete property. If I try to read a property or method not defined by the TodoItem class, the TypeScript compiler will report an error. Similarly, the compiler will report an error if the result of the return statement doesn’t match the result type declared by the method.
The new statement calls the getTodoItems method defined in Listing 1-19 and uses the standard JavaScript forEach method to write a description of each TodoItem object using the console object.
The final feature I need for the TodoCollection class is to provide counts of the total number of TodoItem objects, the number that are complete, and the number still outstanding.
TypeScript doesn’t get in the way of using JavaScript code, and the changes in Listing 1-29 make use of the Inquirer.js package to prompt the user and offer a choice of commands. There is only one command available currently, which is Quit, but I’ll add more useful features shortly.
I don’t describe the Inquirer.js API in detail in this book because it is not directly related to TypeScript. See https://github.com/SBoudrias/Inquirer.js for details if you want to use Inquirer.js in your own projects.
The inquirer.prompt method is used to prompt the user for a response and is configured using a JavaScript object. The configuration options I have chosen present the user with a list that can be navigated using the arrow keys, and a selection can be made by pressing Return. When the user makes a selection, the function passed to the then method is invoked, and the selection is available through the answers.command property.

Prompting the user for a command
If you press the Return key, the Quit command will be selected, and the application will terminate.
TypeScript doesn’t prevent JavaScript code from being used, but it isn’t able to provide any assistance for its use. The compiler doesn’t have any insight into the data types that are being used by Inquirer.js and has to trust that I am using the right types of arguments to prompt the user and that I am processing the response objects safely.
There are two ways to provide TypeScript with the information that it requires for static typing. The first approach is to describe the types yourself. I cover the features that TypeScript provides for describing JavaScript code in Chapter 14. Manually describing JavaScript code isn’t difficult, but it does take some time and requires good knowledge of the code you are describing.
Type declarations are installed using the npm install command, just like JavaScript packages. The save-dev argument is used for packages that are used in development but that are not part of the application. The package name is @types/ followed by the name of the package for which type descriptions are required. For the Inquirer.js package, the type declarations package is @types/inquirer because inquirer is the name used to install the JavaScript package.
See https://github.com/DefinitelyTyped/DefinitelyTyped for the details of the Definitely Typed project and the packages for which type declarations are available.
The type declaration allows TypeScript to provide the same set of features throughout the application, even though the Inquirer.js package is written in pure JavaScript and not TypeScript. However, as this example shows, there can be limitations to this feature, and the addition of a property that isn’t supported has produced an error about the value assigned to the type property. This happens because it can be difficult to describe the types that pure JavaScript expects, and sometimes the error messages can be more of a general indication that something is wrong.
The example application doesn’t do a great deal at the moment and requires additional commands. In the sections that follow, I add a series of new commands and provide the implementation for each of them.

Toggling completed items

Adding a new task
The changes add a new prompt to the application that presents the user with the list of tasks and allows their state to be changed. The showCompleted variable is used to determine whether completed items are shown, creating a link between the Toggle and Complete commands.

Completing items
Lowdb is an excellent database package that stores data in a JSON file and that is used as the data storage component for the json-server package, which I use to create HTTP web services in Part 3 of this book.
I don’t describe the Lowdb API in detail in this book because it is not directly related to TypeScript. See https://github.com/typicode/lowdb for details if you want to use Lowdb in your own projects.
The compiler knows that the tasks argument corresponds to the tasks property in the schema type and that the get operation will return an array of objects with id, task, and complete properties.
When the application starts, a file called Todos.json will be created in the todo folder and used to store a JSON representation of the TodoItem objects, ensuring that changes are not lost when the application is terminated.
In this chapter, I created a simple example application to introduce you to TypeScript development and demonstrate some important TypeScript concepts. You saw that TypeScript provides features that supplement JavaScript, focus on type safety, and help avoid common patterns that trip up developers, especially those coming to JavaScript from languages such as C# or Java.
You saw that TypeScript isn’t used in isolation and that a JavaScript runtime is required to execute the JavaScript code that the TypeScript compiler produces. The advantage of this approach is that projects written with TypeScript have full access to the broad spectrum of JavaScript packages that are available, many of which have type definitions available for easy use.
The application I created in this chapter uses some of the most essential TypeScript features, but there are many more available, as you can tell from the size of this book. In the next chapter, I put TypeScript in context and describe the structure and content of this book.
TypeScript is a superset of the JavaScript language that focuses on producing safe and predictable code that can be executed by any JavaScript runtime. Its headline feature is static typing, which makes working with JavaScript more predictable for programmers familiar with languages such as C# and Java. In this book, I explain what TypeScript does and describe the different features it provides.
The TypeScript team makes frequent releases, which means there is an ongoing stream of fixes and features. It doesn’t seem fair or reasonable to ask you to buy a new edition of this book every few months, especially since most TypeScript features are unlikely to change even in a major release. Instead, I am going to post updates following the major releases to the GitHub repository for this book, https://github.com/Apress/essential-typescript-4.
This is an ongoing experiment for me (and for Apress), and I don’t yet know what form those updates may take—not least because I don’t know what the major releases of TypeScript will contain—but the goal is to extend the life of this book by supplementing the examples it contains.
I am not making any promises about what the updates will be like, what form they will take, or how long I will produce them before folding them into a new edition of this book. Please keep an open mind and check the repository for this book when new TypeScript versions are released. If you have ideas about how the updates could be improved, then email me at adam@adam-freeman.com and let me know.
TypeScript isn’t the solution to every problem, and it is important to know when you should use TypeScript and when it will simply get in the way. In the sections that follow, I describe the high-level features that TypeScript provides and the situations in which they can be helpful.
TypeScript’s headline features are focused on developer productivity, especially through the use of static types, which help make the JavaScript type system easier to work with. Other productivity features, such as access control keywords and a concise class constructor syntax, help prevent common coding errors.

The TypeScript transformation to JavaScript code
The combination of JavaScript and TypeScript features retains much of the flexible and dynamic nature of JavaScript while constraining the use of data types so they are familiar and more predictable for most developers. It also means that projects that use TypeScript can still make use of the wide range of third-party JavaScript packages that are available, either to provide specific features (such as the command-line prompts in Chapter 1) or to embrace complete frameworks for app development (such as the React, Angular, and Vue.js frameworks described in Part 3).
TypeScript features can be applied selectively, which means you can use only those features useful for a specific project. If you are new to TypeScript and JavaScript, you are likely to start by using all of the TypeScript features. As you become more experienced and your depth of knowledge increases, you will find yourself using TypeScript with more focus and applying its features just to the parts of your code that are especially complex or that you expect to cause problems.
Some TypeScript features are implemented entirely by the compiler and leave no trace in the JavaScript code that is executed when the application runs. Other features are implemented by building on standard JavaScript and performing additional checks during compilation. This means you often have to understand how a feature works and how it is implemented to get the best results, which can make TypeScript features seem inconsistent and arcane.
More broadly, TypeScript enhances JavaScript, but the result is still JavaScript, and development in a TypeScript project is largely a process of writing JavaScript code. Some developers adopt TypeScript because they want to write web applications without learning how JavaScript works. They see that TypeScript is produced by Microsoft and assume that TypeScript is C# or Java for web development, which is an assumption that leads to confusion and frustration.
Effective TypeScript requires a good knowledge of JavaScript and the reasons it behaves as it does. Chapters 3 and 4 describe the JavaScript features you need to understand to get the best out of TypeScript and provide a solid foundation for understanding why TypeScript is such a powerful tool.
If you are willing to understand the JavaScript type system, then you will find TypeScript a pleasure to use. But if you are not willing to invest the time to become competent in JavaScript, then you should not use TypeScript. Adding TypeScript to a project when you don’t have any JavaScript knowledge makes development more difficult because you will have two sets of language features to wrangle, neither of which will behave exactly as you expect.
JavaScript has had a turbulent history but has recently become the focus of a concerted standardization and modernization effort, introducing new features that make JavaScript easier to use. The problem is that there are still lots of JavaScript runtimes that don’t support these modern features, especially older browsers, which constrains JavaScript development to the small set of language features that are universally supported. JavaScript can be a challenging language to master, and this is made worse when the features intended to make development easier cannot be used.
The TypeScript compiler can transform JavaScript code written using modern features into code that conforms to older versions of the JavaScript language. This allows recent JavaScript features to be used with TypeScript during development while allowing older JavaScript runtimes to execute the code that the project produces.
The TypeScript compiler does a good job of dealing with most language features, but some features can’t be translated effectively for older runtimes. If the earliest versions of JavaScript are your target, you will find that not all modern JavaScript features can be used during development because the TypeScript compiler doesn’t have the means to represent them in legacy JavaScript.
That said, the need to generate legacy JavaScript code isn’t important in all projects because the TypeScript compiler is just one part of an extended toolchain. The TypeScript compiler is responsible for applying the TypeScript features, but the result is modern JavaScript code that is further processed by other tools. This approach is commonly used in web application development, and you will see examples in Part 3.
If you decide that TypeScript is the right choice for your project, then you should be familiar with using data types in development and understand the basic JavaScript features. Don’t worry if you do not understand how JavaScript deals with data types, however, because I provide a primer for all the JavaScript features that are useful to understand TypeScript in Chapters 3 and 4. In Part 3 of this book, I demonstrate how TypeScript can be used with popular web application development frameworks, and knowledge of HTML and CSS is required for these examples.
The only development tools needed for TypeScript development are the ones you installed in Chapter 1 when you created your first application. Some later chapters require additional packages, but full instructions are provided. If you successfully built the application in Chapter 1, then you are set for TypeScript development and the rest of the chapters in this book.
Part 1, “Getting Started with TypeScript”: Part 1 of this book provides the information you need to get started with TypeScript development. It includes Chapter 1, this chapter, and a primer chapter for the data type features provided by JavaScript. Chapters 5 and 6 introduce the TypeScript development tools.
Part 2, “Understanding TypeScript”: Part 2 of this book covers the TypeScript features for developer productivity, including static types. TypeScript provides a lot of different type features, which I describe in-depth and demonstrate with examples.
Part 3, “Creating Applications with TypeScript”: TypeScript isn’t used on its own, so Part 3 of this book shows you how to use TypeScript to create web applications using three popular frameworks: React, Angular, and Vue.js. These chapters explain the TypeScript features that are useful for each framework and demonstrate how to achieve tasks commonly required during web application development. To provide the foundation for understanding what these frameworks do, I also show you how to create a stand-alone web application that doesn’t rely on a web application framework.
This is a listing from Chapter 7, which shows the contents of a file called index.ts that can be found in the src folder. Don’t worry about the content of the listing or the purpose of the file; just be aware that this type of listing contains the complete contents of a file and that the changes you need to make to follow the example are shown in bold.
This is a listing from Chapter 19, and it shows a set of changes applied to one part of a larger file. When you see a partial listing, you will know that the rest of the file does not have to change and that only the sections marked in bold are different.
In this listing from Chapter 16, the changes are still marked in bold, and the parts of the file that are omitted from the listing are not affected by this example.
You can download the example projects for all the chapters in this book from https://github.com/Apress/essential-typescript-4. The download is available without charge and contains everything that you need to follow the examples without having to type in all of the code.
The first thing to do is to go back to the start of the chapter and begin over. Most problems are caused by accidentally skipping a step or not fully applying the changes shown in a listing. Pay close attention to the emphasis in code listings, which highlights the changes that are required.
Next, check the errata/corrections list, which is included in the book’s GitHub repository. Technical books are complex, and mistakes are inevitable, despite my best efforts and those of my editors. Check the errata list for the list of known errors and instructions to resolve them.
If you still have problems, then download the project for the chapter you are reading from the book’s GitHub repository, https://github.com/Apress/essential-typescript-4, and compare it to your project. I create the code for the GitHub repository by working through each chapter, so you should have the same files with the same contents in your project.
If you still can’t get the examples working, then you can contact me at adam@adam-freeman.com for help. Please make it clear in your email which book you are reading, and which chapter/example is causing the problem. A page number or code listing is always helpful. Please remember that I get a lot of emails and that I may not respond immediately.
You can report errors to me by email at adam@adam-freeman.com, although I ask that you first check the errata/corrections list for this book, which you can find in the book’s GitHub repository at https://github.com/Apress/essential-typescript-4, in case it has already been reported.
I add errors that are likely to confuse readers, especially problems with example code, to the errata/corrections file on the GitHub repository, with a grateful acknowledgment to the first reader who reported it. I keep a list of less serious issues, which usually means errors in the text surrounding examples, and I use them when I write a new edition.
You can email me at adam@adam-freeman.com. It has been a few years since I started publishing an email address in my books. I wasn’t entirely sure that it was a good idea, but I am glad that I did it. I have received emails from around the world, from readers working or studying in every industry, and—for the most part, anyway—the emails are positive, polite, and a pleasure to receive.
I try to reply promptly, but I get many emails, and sometimes I get a backlog, especially when I have my head down trying to finish writing a book. I always try to help readers who are stuck with an example in the book, although I ask that you follow the steps described earlier in this chapter before contacting me.
While I welcome reader emails, there are some common questions for which the answers will always be “no.” I am afraid that I won’t write the code for your new startup, help you with your college assignment, get involved in your development team’s design dispute, or teach you how to program.
Please email me at adam@adam-freeman.com and let me know. It is always a delight to hear from a happy reader, and I appreciate the time it takes to send those emails. Writing these books can be difficult, and those emails provide essential motivation to persist at an activity that can sometimes feel impossible.
You can still email me at adam@adam-freeman.com, and I will still try to help you. Bear in mind that I can help only if you explain what the problem is and what you would like me to do about it. You should understand that sometimes the only outcome is to accept I am not the writer for you and that we will have closure only when you return this book and select another. I’ll give careful thought to whatever has upset you, but after 25 years of writing books, I have come to accept that not everyone enjoys reading the books I like to write.
In this chapter, I explained when TypeScript is a good choice for projects. I also outlined the content and structure of this book, explained where to get the source code, and talked about how to contact me if you have problems with the examples in this book. In the next chapter, I give you a primer for the JavaScript type system, which provides the underpinnings for the features of TypeScript.
Effective TypeScript development requires an understanding of how JavaScript deals with data types. This can be a disappointment to developers who adopt TypeScript because they found JavaScript confusing, but understanding JavaScript makes understanding TypeScript easier and provides valuable insights into what TypeScript offers and how its features work. In this chapter, I introduce the basic JavaScript type features, continuing with more advanced features in Chapter 4.
To prepare for this chapter, create a folder called primer in a convenient location. Open a command prompt, navigate to the primer folder, and run the command shown in Listing 3-1.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
JavaScript has many features that are similar to other programming languages, and developers tend to start with code that looks like the statements in Listing 3-5. Even if you are new to JavaScript, the statements in Listing 3-5 will be familiar.
The building blocks for JavaScript code are statements, which are executed in the order they are defined. The let keyword is used to define variables (as opposed to the const keyword, which defines constant values) followed by a name. The value of a variable is set using the assignment operator (the equal sign) followed by a value.
JavaScript provides some built-in objects to perform common tasks, such as writing strings to the command prompt with the console.log method. Strings can be defined as literal values, using single or double quotes, or as template strings, using backtick characters and inserting expressions into the template using the dollar sign and braces.
Most developers will notice that the value for hatPrice has been expressed as a number, while the bootsPrice value is a string of characters, enclosed in double quotes. But in most languages, performing operations on different types would be an error. JavaScript is different; comparing a string and a number succeeds, but trying to total the values actually concatenates them. Understanding the results from Listing 3-6—and the reasons behind them—reveals the details of how JavaScript approaches data types and why TypeScript can be so helpful.
Name | Description |
|---|---|
number | This type is used to represent numeric values. Unlike other programming languages, JavaScript doesn’t differentiate between integer and floating-point values, both of which can be represented using this type. |
string | This type is used to represent text data. |
boolean | This type can have true and false values. |
symbol | This type is used to represent unique constant values, such as keys in collections. |
null | This type can be assigned only the value null and is used to indicate a nonexistent or invalid reference. |
undefined | This type is used when a variable has been defined but has not been assigned a value. |
object | This type is used to represent compound values, formed from individual properties and values. |
The first six types in the table are the JavaScript primitive data types. The primitive types are always available, and every value in a JavaScript application either is a primitive type itself or is composed from primitive types. The sixth type is object and is used to represent objects.
Changing the value assigned to a variable changes the type reported by the typeof keyword because values have types. The type of the value initially assigned to myVariable was string, and then the variable was assigned a number value. This dynamic approach to types is made easier by the limited range of types that JavaScript supports, which makes it easier to determine which of the built-in types is being used. For example, all numbers are represented by the number type, which means that integers and floating-point values are all handled using number, which would not be possible with a more complex set of types.
When the typeof keyword is used on null values, the result is object. This is a long-standing behavior that dates back to the earliest days of JavaScript and that hasn’t been changed because so much code has been written that expects this behavior.
The double equal sign performs a comparison using type coercion so that JavaScript will try to convert the values it is working with in order to produce a useful result. This is known as the JavaScript abstract equality comparison, and when a number is compared to a string, the string value is converted to a number value, and then the comparison is performed. This means when the number value 100 is compared with the string value 100, the string is converted to the number value 100, and this is the reason why the if expression evaluates to true.
You can read the sequence of steps that JavaScript follows in an abstract equality comparison in the JavaScript specification, https://www.ecma-international.org/ecma-262/7.0/#sec-abstract-equality-comparison. The specification is well-written and surprisingly interesting. But before you spend a day getting lost in the implementation details, you should bear in mind that TypeScript constrains the use of some of the most unusual and exotic features.
When you use the + operator on a number and a string, one of the values is converted. The confusing part is that the conversion isn’t the same as for comparisons. If either of the values is a string, the other value is converted to a string, and both string values are concatenated. This means that when the number value 100 is added to the string value 100, the number is converted to a string and concatenated to produce the string result 100100.
The double equal sign (==) performs a comparison that applies type coercion. The triple equal sign (===) applies a strict comparison that will return true only if the values have the same type and are equal.
The value of the variable named secondCity is set with an expression that checks the firstCity value: if firstCity is converted to the boolean value true, then the value of secondCity will be the value of firstCity.
The undefined type is used when variables are defined but have not been assigned a value, which is the case for the variable named firstCity, and the use of the || operator ensures that the fallback value for secondCity will be used when firstCity is undefined or null.
A function’s parameter types are determined by the values used to invoke the function. A function may assume that it will receive number values, for example, but there is nothing to prevent the function from being invoked with string, boolean, or object arguments. Unexpected results can be produced if the function doesn’t take care to validate its assumptions, either because the JavaScript runtime coerces values or because features specific to a single type are used.
The first function call includes a string argument, which causes all of the function’s parameters to be converted to string values and concatenated, meaning that the function returns the string value 100100undefined.
The second function call uses three number values, which are added together and produce the number result 600. The final function call uses number arguments but doesn’t provide a third value, which causes an undefined parameter. JavaScript coalesces undefined to the special number value NaN (meaning not a number). The result of addition that includes NaN is NaN, which means that the type of the result is number but the value isn’t useful and is unlikely to be what was intended.
Although the results in the previous section can confuse, they are the outcomes described in the JavaScript specification. The problem isn’t that JavaScript is unpredictable but that its approach is different from other popular programming languages.
There are three parts to an arrow function: the input parameters, then an equal sign with a greater-than sign (the “arrow”), and finally the result value. The return keyword and curly braces are required only if the arrow function needs to execute more than one statement.
Functions—regardless of which syntax is used—are values, too. They are a special category of the object type, described in the “Working with Objects” section, and functions can be assigned to variables passed as arguments to other functions and used like any other value.
In Listing 3-20, the arrow syntax is used to define a function that is assigned a variable called sumPrices. Functions are special because they can be invoked, but being able to treat functions as values allows complex functionality to be expressed concisely, although it is easy to create code that can be difficult to read. There are more examples of arrow functions and using functions as values throughout the book.
The size of an array is not specified when it is created, and capacity will be allocated automatically as items are added or removed. JavaScript arrays are zero-based and are defined using square brackets, optionally with the initial contents separated by commas. The names array in the example is created with three string values. The prices array is created empty, and the push method is used to append items to the end of the array.
Method | Description |
|---|---|
concat(otherArray) | This method returns a new array that concatenates the array on which it has been called with the array specified as the argument. Multiple arrays can be specified. |
join(separator) | This method joins all the elements in the array to form a string. The argument specifies the character used to delimit the items. |
pop() | This method removes and returns the last item in the array. |
shift() | This method removes and returns the first element in the array. |
push(item) | This method appends the specified item to the end of the array. |
unshift(item) | This method inserts a new item at the start of the array. |
reverse() | This method returns a new array that contains the items in reverse order. |
slice(start,end) | This method returns a section of the array. |
sort() | This method sorts the array. An optional comparison function can be used to perform custom comparisons. Alphabetic sorting is performed if no comparison function is defined. |
splice(index, count) | This method removes count items from the array, starting at the specified index. The removed items are returned as the result of the method. |
every(test) | This method calls the test function for each item in the array and returns true if the function returns true for all of them and false otherwise. |
some(test) | This method returns true if calling the test function for each item in the array returns true at least once. |
filter(test) | This method returns a new array containing the items for which the test function returns true. |
find(test) | This method returns the first item in the array for which the test function returns true. |
findIndex(test) | This method returns the index of the first item in the array for which the test function returns true. |
forEach(callback) | This method invokes the callback function for each item in the array, as described in the previous section. |
includes(value) | This method returns true if the array contains the specified value. |
map(callback) | This method returns a new array containing the result of invoking the callback function for every item in the array. |
reduce(callback) | This method returns the accumulated value produced by invoking the callback function for every item in the array. |
Care is required when using objects because they may not have the shape (the term used for the combination of properties and values) that you expect or that was originally used when the object was created.
The code can be difficult to read, but the ?? operator will coerce undefined and null values to false and other values to true. The checks can be used to provide a fallback for an individual property, for an object, or for a combination of both.
The optional changing operator (the ? character) will stop evaluating an expression if the value it is applied to is null or undefined. In the listing, I have applied the operator to hat, which means that the expression won’t try to read the value of the price property if hat is undefined or null. The result is that the fallback value will be used if hat or hat.price is undefined or null.
The properties defined by the hat object are decomposed. The hat.price property is assigned to the new price property, and all the other properties are assigned to the someProperties object.
The example introduces a priceIncTax property whose value is updated automatically when the price property is set. The hat object does this by using a getter and setter for the price property to update a backing property named _price. When a new value is assigned to the price property, the setter updates the backing property and the priceIncTax property. When the value of the price property is read, the getter responds with the value of the _price property. (A backing property is required because getters and setters are treated as properties and cannot have the same name as any of the conventional properties defined by the object.)
JavaScript doesn’t have any built-in support for private properties, meaning properties that can be accessed only by an object’s methods, getters, and setters. There are techniques to achieve a similar effect, but they are complex, and so the most common approach is to use a naming convention to denote properties not intended for public use. This doesn’t prevent access to these properties, but it does at least make it obvious that doing so is undesirable. A widely used naming convention is to prefix the property name with an underscore, as demonstrated with the _price property in Listing 3-32.
There is a proposal working its way through the standardization process to add support for private properties to the JavaScript language. The names of private properties will be prefixed with the # character, but it will be some time before this feature is part of the JavaScript standard. TypeScript provides private properties and supports the JavaScript feature, as described in Chapter 11.
The this keyword can be confusing to even experienced JavaScript programmers. In other programming languages, this is used to refer to the current instance of an object created from a class. In JavaScript, the this keyword can often appear to work the same way—right up until the moment a change breaks the application and undefined values start to appear.
Understanding why this happens and being able to fix the problem requires taking a step back and examining what the this keyword really does in JavaScript.
JavaScript defines a global object, which can be assigned values that are available throughout an application. The global object is used to provide access to the essential features in the execution environment, such as the document object in browsers that allows interaction with the Document Object Model API.
Values assigned names without using the let, const, or var keyword are assigned to the global object. The statement that assigns the string value Hello creates a variable in the global scope. When the function is executed, this is assigned the global object, so reading this.greeting returns the string value Hello, explaining the output produced by the application.
As explained earlier, functions are objects, which means they define methods, including the call method. It is this method that is used to invoke a function behind the scenes. The first argument to the call method is the value for this, which is set to the global object. This is the reason that this can be used in any function and why it returns the global object by default.
The name of the global object changes based on the execution environment. In code executed by Node.js, global is used, but window or self may be required in browsers. At the time of writing, there is a proposal to standardize the name global, but it has yet to be adopted universally.
JavaScript supports strict mode, which disables or restricts features that have historically caused poor-quality software or that prevent the runtime from executing code efficiently. When strict mode is enabled, the default value for this is undefined to prevent accidental use of the global object, and values with global scope must be explicitly defined as properties on the global object. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode for details. The TypeScript compiler provides a feature for automatically enabling strict mode in the JavaScript code it generates, as described in Chapter 5.
The value of this is always set to myObject, even when the writeMessage function is invoked as a stand-alone function.
The combined statement is a little harder to read, but it helps emphasize that the value of this is based on how a function is invoked. The getWriter method is invoked through myObject and means that the value of this will be set to myObject. When the arrow function is invoked, it finds a value of this from the getWriter function. The result is that when the getWriter method is invoked through myObject, the value of this in the arrow function will be myObject, and the this.greeting expression in the template string will be Hi, there.
With these changes, the value of this for the writeDetails method will be its enclosing object, regardless of how it is invoked.
In this chapter, I introduced the basic features of the JavaScript type system. These are features that often confuse because they work differently from those in other programming languages. Understanding these features make working with TypeScript easier because they provide insight into the problems that TypeScript solves. In the next chapter, I describe more of the JavaScript type features that are useful for understanding TypeScript.
In this chapter, I continue describing the JavaScript type features that are important to TypeScript development. I focus on the JavaScript support for objects, the different ways they can be defined, and how they relate to JavaScript classes. I also demonstrate the features for handling sequences of values, the JavaScript collections, and the modules feature, which allows a project to be split up into multiple JavaScript files.
In this chapter, I continue to use the primer project created in Chapter 3. To prepare for this chapter, replace the contents of the index.js file in the primer folder with the code shown in Listing 4-1.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
JavaScript objects have a link to another object, known as the prototype, from which they inherit properties and methods. Since prototypes are objects and have their own prototype, objects form an inheritance chain that allows complex features to be defined once and used consistently.

An object and its prototype
Name | Description |
|---|---|
getPrototypeOf | This method returns an object’s prototype. |
setPrototypeOf | This method changes the prototype of an object. |
getOwnPropertyNames | This method returns the names of an object’s own properties. |

Objects and a common prototype
Changes to Object should be made cautiously because they affect all the other objects in the application. The new toString function in Listing 4-5 produces more useful output for the hat and boots objects but assumes that there will be name and price properties, which won’t be the case when toString is called on other objects.

A chain of prototypes
The result is the same as the previous example, but using a constructor function can help ensure that objects are created consistently and have their prototypes set correctly.
The call method allows the new object to be passed to the next constructor through the this value.

A more complex prototype chain
A prototype can override a property or method by using the same name as one defined further along the chain. This is also known as shadowing in JavaScript, and it takes advantage of the way that the JavaScript runtime follows the chain.
This method gets a result from the Product prototype’s toString method and combines it with additional data in a template string.
Notice that the instanceof operator is used with the constructor function. The Object.isPrototypeOf method is used directly with prototypes, which can be useful if you are not using constructors.
Classes are defined with the class keyword, followed by a name for the class. The class syntax may appear more familiar, but classes are translated into the underlying JavaScript prototype system described in the previous section.
Generators are consumed in the same way as iterators. The JavaScript runtime creates the next function and executes the generator function until it reaches the yield keyword, which provides a value in the sequence. Execution of the generator function continues gradually each time the next function is invoked. When there are no more yield statements to execute, an object whose done property is true is created automatically.
The GiftPack class keeps track of a set of related products. One of the methods defined by GiftPack is named getGenerator and is a generator that yields the products.
The asterisk appears before generator method names.
I no longer have to invoke a method to get a generator, which produces clearer and more elegant code.
Traditionally, collections of data in JavaScript have been managed using objects and arrays, where objects are used to store data by key, and arrays are used to store data by index. JavaScript also provides dedicated collection objects that provide more structure, although they can also be less flexible, as explained in the sections that follow.
Name | Description |
|---|---|
Object.keys(object) | This method returns an array containing the property names defined by the object. |
Object.values(object) | This method returns an array containing the property values defined by the object. |
Name | Description |
|---|---|
set(key, value) | This method stores a value with the specified key. |
get(key) | This method retrieves the value stored with the specified key. |
keys() | This method returns an iterator for the keys in the Map. |
values() | This method returns an iterator for the values in the Map. |
entries() | This method returns an iterator for the key/value pairs in the Map, each of which is presented as an array containing the key and value. This is the default iterator for Map objects. |
The main advantage of using a Map is that any value can be used as a key, including Symbol values. Each Symbol value is unique and immutable and ideally suited as an identifier for objects. Listing 4-21 defines a new Map that uses Symbol values as keys.
Symbol values can be useful, but they can be difficult to work with because they are not human-readable and have to be created and handled carefully. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol for more details.
Name | Description |
|---|---|
add(value) | This method adds the value to the Set. |
entries() | This value returns an iterator for the items in the Set, in the order in which they were added. |
has(value) | This value returns true if the Set contains the specified value. |
forEach(callback) | This method invokes a function for each value in the Set. |
Most applications are too complex to have all the code in a single file. To break up an application into manageable chunks, JavaScript supports modules. There have been competing approaches for defining and consuming modules, but the approach I focus on here is the one defined by the JavaScript specification, which is the most broadly supported by popular JavaScript development tools and application frameworks.
The function defined in the tax.js file receives a price value and applies a 20 percent tax rate. The function itself is simple, and it is the export and default keywords that are important. The export keyword is used to denote the features that will be available outside the module. By default, the contents of the JavaScript file are private and must be explicitly shared using the export keyword before they can be used in the rest of the application. The default keyword is used when the module contains a single feature, such as the function defined in Listing 4-25. Together, the export and default keywords are used to specify that the only function in the tax.js file is available for use in the rest of the application.
The import keyword is used to declare a dependency on the module. The import keyword can be used in several different ways, but this is the format you will use most often when working with modules you have created within your project.
The import keyword is followed by an identifier, which is the name by which the function in the module will be known when it is used, and the identifier in this example is calcTax. The from keyword follows the identifier, which is then followed by the location of the module. It is important to pay close attention to the location because different behaviors are created by different location formats, as described in the “Understanding Module Locations” sidebar.
This location tells the build tools that there is a dependency on the tax module, which can be found in the same folder as the file that contains the import statement. Notice that the file extension is not included in the location.
The location for this import statement doesn’t start with a period and will be interpreted as a dependency on the react module in the project’s node_modules folder, which is the package that provides the core React application features.
In this chapter, I described the JavaScript features for dealing with objects, sequences of values, collections, and the use of modules. These are all JavaScript features, but, as you will learn, understanding them helps put TypeScript into context and sets the foundation for effective TypeScript development. In the next chapter, I introduce the TypeScript compiler, which is at the heart of the features that TypeScript provides to developers.
In this chapter, I show you how to use the TypeScript compiler, which is responsible for transforming TypeScript code into JavaScript that can be executed by browsers or the Node.js runtime. I also describe the compiler configuration options that are most useful for TypeScript development, including those that are used with the web application frameworks covered in Part 3 of this book.
The install argument tells NPM to download and add a package to the current folder. The --save-dev argument tells NPM that these are packages for use in development but not part of the application. The final argument is the name of the package, followed by the @ symbol, followed by the version that is required.
It is important to use the versions specified for the examples in this book. You may encounter unexpected behavior or errors if you use different versions.

The contents of the example project folder
Name | Description |
|---|---|
dist | This folder contains the output from the compiler. |
node_modules | This folder contains the packages that the application and development tools require, as described in the “Using the Node Package Manager” section. |
src | This folder contains the source code files that will be compiled by the TypeScript compiler. |
package.json | This folder contains the set of top-level package dependencies for the project, as described in the “Using the Node Package Manager” section. |
package-lock.json | This file contains a complete list of the package dependencies for the project. |
tsconfig.json | This file contains the configuration settings for the TypeScript compiler. |
TypeScript and JavaScript development depends on a rich ecosystem of packages. Most TypeScript projects will require packages that contain the TypeScript compiler, the application framework (if one is used), and the tools required to package the compiled code so that it can be distributed and executed.
NPM is used to download these packages and add them to the project’s node_modules folder. Each package declares a set of dependencies on other packages and specifies the versions that it can work with. NPM follows this chain of dependencies, working out which versions of each package is needed and downloads everything that is required.
Name | Description |
|---|---|
tsc-watch | This package watches a source code folder, runs the TypeScript compiler when there is a change, and executes the compiled JavaScript code. |
typescript | This is the package that contains the TypeScript compiler and its supporting tools. |
Package managers can install packages so they are specific to a single project (known as a local install) or so they can be accessed from anywhere (known as a global install). In Chapter 1, you installed the typescript package globally, which allows the tsc command to be used to compile code anywhere. In Listing 5-2, the same package is installed locally, even though the functionality is already available. This is so that other packages in the same project can access the functionality provided by the TypeScript compiler.
Format | Description |
|---|---|
4.2.2 | Expressing a version number directly will accept only the package with the exact matching version number, e.g., 4.2.2. |
* | Using an asterisk accepts any version of the package to be installed. |
>4.2.2 >=4.2.2 | Prefixing a version number with > or >= accepts any version of the package that is greater than or greater than or equal to a given version. |
<4.2.2 <=4.2.2 | Prefixing a version number with < or <= accepts any version of the package that is less than or less than or equal to a given version. |
~4.2.2 | Prefixing a version number with a tilde (the ~ character) accepts versions to be installed even if the patch level number (the last of the three version numbers) doesn’t match. For example, specifying ~4.2.2 will accept version 4.2.3 or 4.2.4 (which would contain patches to version 4.2.2) but not version 4.3.0 (which would be a new minor release). |
^4.2.2 | Prefixing a version number with a caret (the ^ character) will accept versions even if the minor release number (the second of the three version numbers) or the patch number doesn’t match. For example, specifying ^4.2.2 will allow versions 4.2.3 and 4.3.0, but not version 5.0.0. |
Command | Description |
|---|---|
npm install | This command performs a local install of the packages specified in the package.json file. |
npm install package@version | This command performs a local install of a specific version of a package and updates the package.json file to add the package to the dependencies section. |
npm install --save-dev package@version | This command performs a local install of a specific version of a package and updates the package.json file to add the package to the devDependencies section, which is used to add packages to the project that are required for development but are not part of the application. |
npm install --global package@version | This command will perform a global install of a specific version of a package. |
npm list | This command will list all the local packages and their dependencies. |
npm run | This command will execute one of the scripts defined in the package.json file. |
npx package | This command runs the code contained in a package. |
The node_modules folder is typically excluded from version control because it contains a large number of files and because packages can contain platform-specific components that don’t work when a project is checked out on a new machine. Instead, the npm install command is used to create a new node_modules folder and install the required packages.
This approach can produce a different set of packages each time the npm install command is run because dependencies can be expressed as a range of versions, as described in Table 5-4. To ensure consistency, NPM creates the package-lock.json file, which contains a complete list of the packages installed in the node_module folder, along with the versions that were used. The package-lock.json file is updated by NPM when changes are made to the packages in the project and the versions it contains are used by the npm install command.
The package.json and package-lock.json files should be checked in for revision control to ensure everyone on the development team gets the same packages. When you pull updates from the repository, make sure you run the npm install command to receive any new packages that have been added by another developer.
The TypeScript compiler, tsc, is responsible for compiling TypeScript files. It is the compiler that is responsible for implementing TypeScript features, such as static types, and the result is pure JavaScript from which the TypeScript keywords and expressions have been removed.
Name | Description |
|---|---|
compilerOptions | This section groups the settings that the compiler will use, as described in the “Useful Compiler Configuration Settings” section. |
files | This setting specifies the files that will be compiled, which overrides the default behavior where the compiler searches for files to compile. |
include | This setting is used to select files for compilation by pattern. If unspecified, files with the .ts, tsx, and .d.ts extensions will be selected. (TSX files are described in Chapter 15. Files with the .d.ts extension are described in Chapter 14.) |
exclude | This setting is used to exclude files from the compilation by pattern. |
compileOnSave | When set to true, this setting is a hint to the code editor that it should run the compiler each time a file is saved. This feature is not supported by all editors, and the watch feature, described in the next section, provides a more useful alternative. |
The files displayed by the listFiles option include the type declarations that the compiler has located. As explained in Chapter 1, type declarations describe the data types used by JavaScript code so that it can be safely used in a TypeScript application. The TypeScript package includes type declarations for different versions of the JavaScript language and for the APIs that are available in Node.js and browsers. Type declarations are described in more detail in Chapter 14, and these specific files are described in the “Using the Version Targeting Feature” section of this chapter.
The paths for the type declaration files are outside of the project because the tsc command runs the TypeScript compiler from the package installed globally in Chapter 1. The same package has been installed locally in the node_modules folder and is used when creating a development pipeline, as described in the next section. If you need to run the compiler from the package installed locally in the project, then you can use the npx command, such that npx tsc --listFiles has the same effect as the command in Listing 5-7 but uses the local package.
As part of the discovery process, the TypeScript compiler looks for TypeScript files in the location specified by the rootDir setting in the tsconfig.json file. The compiler examines the src folder and discovers the index.ts file.
The index.js file contains the compiled code from the index.ts file in the src folder but without the additional type information for the printMessage function. The relationship between the TypeScript code and the JavaScript code the compiler produces won’t always be as direct, especially when the compiler has been instructed to target a different version of JavaScript, as described in the “Using the Version Targeting Feature” section.
Do not edit the JavaScript files in the dist folder because your changes will be overwritten the next time the TypeScript compiler runs. Changes must be made only to the TypeScript files.
Run the command shown in Listing 5-10 in the tools folder to execute the compiler.
The printMessage function specifies the data type it is willing to accept through its msg parameter using a type annotation, which is described in Chapter 7. For this chapter, it is enough to know that invoking the printMessage function with a number value is a TypeScript error.
When the compiler runs, output will be generated only when there are no errors detected in the JavaScript code.
The compiler will start, report the same error as shown in the previous section, and then start monitoring the project for code changes. To trigger a compile, comment out the problem statement added to the index.ts file, as shown in Listing 5-13.
You may encounter a bug in Node.js when running the TypeScript compiler in watch mode. If you see a Check failed: U_SUCCESS(status) error, then may need to update to the latest version of Node.js. Alternatively, just jump ahead to the next section because the TypeScript compiler watch mode is used only in this part of the chapter and not relied on again in this book.
The compiler’s watch mode doesn’t automatically execute compiled code. It can be tempting to combine the watch mode with a tool that executes a command when a file change is detected, but this can be difficult because the JavaScript files are not all written at the same time and there is no easy way to reliably determine when compilation has completed.
Pay close attention to the order of the escape characters, the double quotes (the " character), and the back ticks (the ` character).
The onsuccess argument specifies a command that is executed when compilation succeeds without errors. Make the change shown in Listing 5-16 to the index.ts file to trigger a compilation and execute the result.
See https://github.com/gilamran/tsc-watch for details of the other options provided by the ts-watch package.
The TypeScript compiler also provides an API that can be used to create custom tools, which can be useful if you need to integrate the compiler into a complex workflow. Microsoft doesn’t provide extensive documentation for the API, but there are some notes and examples at https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API.
The effect is the same, but the compiler can now be started without having to remember the combination of package and filenames, which can become complex in real projects.
TypeScript relies on the most recent versions of the JavaScript language, which introduced features such as classes. To make it easier to adopt TypeScript, the compiler can generate JavaScript code that targets older versions of the JavaScript language, which means that recent features can be used during development to create code that can be executed by older JavaScript runtimes, such as legacy browsers.
Name | Description |
|---|---|
ES3 | This value targets the third edition of the language specification that was defined in December 1999 and is considered to be the baseline for the language. This is the default value when the target setting is not defined. |
ES5 | This value targets the fifth edition of the language specification that was defined in December 2009 and focuses on consistency. (There was no fourth edition.) |
ES6 | This value targets the sixth edition of the language specification and added features required for creating complex applications, such as classes and modules, arrow functions, and promises. |
ES2015 | This value is equivalent to ES6. |
ES2016 | This value targets the seventh edition of the language specification, which introduced the includes method for arrays and an exponentiation operator. |
ES2017 | This value targets the eighth edition of the language specification, which introduced features for inspecting objects and new keywords for asynchronous operations. |
ES2018 | This value targets the ninth edition of the language specification, which introduced the spread and rest operators and improvements for string handling and asynchronous operations. |
ES2019 | This value targets the tenth edition of the language specification, which includes new array features, changes to error handling, and improvements to JSON formatting. |
ES2020 | This value targets the 11th edition of the language specification, which includes support for the nullish operator, optional chaining, and loading modules dynamically. |
esNext | This value refers to the features that are expected to be included in the next edition of the specification. The specific features supported by the TypeScript compiler can change between releases. This is an advanced setting that should be used with caution. |
The ES in these settings refers to ECMAScript, which is the standard that defines the features implemented by the JavaScript language. The history of JavaScript and ECMAScript is long, tortured, and not at all interesting. For TypeScript development, JavaScript and ECMAScript can be regarded as being the same, which is how I have approached them in the book. See https://en.wikipedia.org/wiki/ECMAScript if you want to get into the details.
The earlier versions of the ECMAScript standard were given numbers, but recent versions are named for the year in which they were completed. This change happened partway through the definition of ES6, which is why it is known as both ES6 and ES2015. The biggest changes to the language were introduced in ES6/ES2015, which can be regarded as the start of “modern” JavaScript. The release of ES6 marked the switch to annual updates to the language specification, which is why the 2016–2020 editions contain only a small number of changes.
The output from the listFiles compiler option showed the files that the compiler discovers and included a series of type declaration files. These files provide the compiler with type information about the features available in different versions of JavaScript and the features provided for applications running in the browser, which are able to create and manage HTML content using the Document Object Model (DOM) API.
Name | Description |
|---|---|
ES5, ES2015, ES2016, ES2017, ES2018, ES2019, ES2020 | These values select type definition files that correspond to a specific version of the JavaScript specification. The old naming scheme can be used as well so that the value ES6 can be used in place of ES2015. |
ESnext | This value selects features that are proposed additions to the JavaScript specification but have not yet been formally adopted. The set of features will change over time. |
dom | This value selects type information files for the Document Object Model (DOM) API that web applications use to manipulate the HTML content presented by browsers. This setting is also useful for Node.js applications. |
dom.iterable | This value provides type information for the additions to the DOM API that allow iteration over HTML elements. |
scriptHost | This value selects type information for the Windows Script Host, which allows for automation on Windows systems. |
webworker | This value selects type information for the web worker feature, which allows web applications to perform background tasks. |
Name | Description |
|---|---|
es2015.core | This setting includes type information for the main features introduced by ES2015. |
es2015.collection | This setting includes type information for the Map and Set collections, described in Chapters 4 and 13. |
es2015.generator es2015.iterable | These settings include type information for the generator and iterator features described in Chapter 4 and 13. |
es2015.promise | This setting includes type information for promises, which describe asynchronous actions. |
es2015.reflect | This setting includes type information for the reflection features that provide access to properties and prototypes, as described in Chapter 16. |
es2015.symbol es2015.symbol.wellknown | These settings include type information about symbols, which are described in Chapter 4. |
It is important to think through the implications of using the lib configuration setting because it just tells the TypeScript compiler that the runtime for the application can be relied on to support a specific set of features, such as the Map in this case. The compiler can adapt the JavaScript it generates for different language features, but that doesn’t extend to objects like collections. Changing the lib setting tells the compiler that there will be a nonstandard set of features available when the compiled JavaScript is executed, and it is your responsibility to ensure this is the case, either because you know more about the runtime than the compiler or because the application uses a polyfill such as core-js (https://github.com/zloirock/core-js).
The set of types I have selected includes the standard types for the version of JavaScript selected by the target property, the dom setting (which provides access to the console object), and the ES2015 collections feature from Table 5-8.
This example runs because the Node.js version used in this book supports the Map feature. In this situation, I knew more about the runtime than the TypeScript compiler, and changing the lib setting produces an example that runs, although the same effect could have been achieved by changing the target setting to a more recent JavaScript version that the compiler knows includes collections. If I were targeting a runtime that supported only ES5, then I would have to provide a polyfill implementation of Map, such as the one included in the core-js package.
The TypeScript compiler uses the target configuration property to select the approach taken to deal with modules. When the target is es5, it uses the commonjs module style, which was the result of an earlier attempt to introduce a module standard. The Node.js runtime supports the commonjs module system by default, which is why the code generated by the TypeScript compiler executes without problems.
Name | Description |
|---|---|
None | This value disables modules. |
CommonJS | This value selects the CommonJS module format, which is supported by Node.js. |
AMD | This value selects the Asynchronous Module Definition (AMD), which is supported by the RequireJS module loader. |
System | This value selects the module format supported by the SystemJS module loader. |
UMD | This value selects the Universal Module Definition (UMD) module format. |
ES2015, ES6 | This value selects the module format specified in the ES2016 language specification. |
ES2020 | This value selects the module format specified in the ES2020 language specification, which includes dynamic loading of modules. |
ESNext | This value selects the module features that have been proposed for the next version of the JavaScript language. |
The choice of module format is driven by the environment that will execute the code. At the time of writing, Node.js supports CommonJS modules and ECMAScript modules, although it requires the file extension to be included in the import statement, which is problematic when using TypeScript, which deals with .ts and .js files.
For web applications, especially those built using a framework like React, Angular, or Vue.js, the module format will be dictated by the framework’s toolchain, which will include either a bundler, which packages up all of the modules into a single JavaScript file during deployment, or a module loader, which sends HTTP requests to the web server to get JavaScript files as they are required. You will see examples of using the TypeScript compiler with these frameworks in Part 3. To target a recent version of JavaScript on Node.js, I have to select the commonjs format, as shown in Listing 5-26.
An alternative approach is to use a third-party package to add support for ES2015 modules to Node.js, which is the approach I took in Chapter 4.
The TypeScript compiler can use two different approaches to resolving dependencies on modules, which it selects based on the module format that is being used. The two modes are classic, which searches for modules in the local project, and Node, which locates modules in the node_modules folder. The TypeScript compiler uses the classic resolution mode when the module property is set to ES2015, System, or AND. For all other module settings, the Node resolution is used. A resolution style can be specified using the moduleResolution configuration property in the tsconfig.json file using the classic or node value.
The TypeScript compiler supports a large number of configuration options. In Part 2, I include a table at the start of each chapter that lists the compiler settings used by the features in the examples. For quick reference, Table 5-10 lists the compiler options used in this book. Many of these options won’t make sense at the moment, but each one is described when it is used, and all will make sense by the end of this book.
See https://www.typescriptlang.org/docs/handbook/compiler-options.html for the complete set of options the compiler supports.
Name | Description |
|---|---|
allowJs | This option includes JavaScript files in the compilation process. |
allowSyntheticDefaultImports | This option allows imports from modules that do not declare a default export. This option is used to increase code compatibility. |
baseUrl | This option specifies the root location used to resolve module dependencies. |
checkJs | This option tells the compiler to check JavaScript code for common errors. |
declaration | This option produces type declaration files, which provide type information for JavaScript code. |
downlevelIteration | This option enables support for iterators when targeting older versions of JavaScript. |
emitDecoratorMetadata | This option includes decorator metadata in the JavaScript emitted by the compiler and is used with the experimentalDecorators option. |
esModuleInterop | This option adds helper code for importing from modules that do not declare a default export and is used in conjunction with the allowSyntheticDefaultImports option. |
experimentalDecorators | This option enables support for decorators. |
forceConsistentCasingInFileNames | This option ensures that names in import statements match the case used by the imported file. |
importHelpers | This option determines whether helper code is added to the JavaScript to reduce the amount of code that is produced overall. |
isolatedModules | This option treats each file as a separate module, which increases compatibility with the Babel tool. |
jsx | This option specifies how HTML elements in JSX/TSX files are processed. |
jsxFactory | This option specifies the name of the factory function that is used to replace HTML elements in JSX/TSX files. |
lib | This option selects the type declaration files the compiler uses. |
module | This option specifies the format used for modules. |
moduleResolution | This option specifies the style of module resolution that should be used to resolve dependencies. |
noEmit | This option prevents the compiler from emitting JavaScript code, with the result that it only checks code for errors. |
noImplicitAny | This option prevents the implicit use of the any type, which the compiler uses when it can’t infer a more specific type. |
noImplicitReturns | This option requires all paths in a function to return a result. |
noUncheckedIndexedAccess | This option does not allow properties accessed via an index signature to be accessed until they have been guarded against undefined values. |
noUnusedParameters | This option causes the compiler to produce a warning if a function defines parameters that are not used. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
paths | This option specifies the locations used to resolve module dependencies. |
resolveJsonModule | This option allows JSON files to be imported as though they were modules. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
skipLibCheck | This option speeds up compilation by skipping the normal checking of declaration files. |
sourceMap | This option determines whether the compiler generates source maps for debugging. |
strict | This option enables stricter checking of TypeScript code. |
strictNullChecks | This option prevents null and undefined from being accepted as values for other types. |
suppressExcessPropertyErrors | This option prevents the compiler from generating errors for objects that define properties not in a specified shape. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
typeRoots | This option specifies the root location that the compiler uses to look for declaration files. |
types | This option specifies a list of declaration files to include in the compilation process. |
In this chapter, I introduced the TypeScript compiler, which is responsible for transforming TypeScript code into pure JavaScript. I explained how the compiler is configured, demonstrated the different ways that it can be used, and showed you how to change the version of the JavaScript language that is targeted and how to change the way that modules are resolved. I finished this chapter by listing the configuration options used in this book, which may not make sense now but will become clearer as you progress through the examples. In the next chapter, I continue with the theme of TypeScript developer tools and explain how to perform debugging and unit testing of TypeScript code.
In this chapter, I continue the theme of TypeScript development tools started in Chapter 5, which introduced the TypeScript compiler. I show you the different ways that TypeScript code can be debugged, demonstrate the use of TypeScript and the linter, and explain how to set up unit testing for TypeScript code.
For this chapter, I continue using the tools project created in Chapter 5. No changes are required for this chapter.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
The TypeScript compiler does a good job of reporting syntax errors or problems with data types, but there will be times when you have code that compiles successfully but doesn’t execute in the way you expected. Using a debugger allows you to inspect the state of the application as it is executing and can reveal why problems occur. In the sections that follow, I show you how to debug a TypeScript application that is executed by Node.js. In Part 3, I show you how to debug TypeScript web applications.
When the compiler next compiles the TypeScript files, it will also generate a map file, which has the map file extension, alongside the JavaScript files in the dist folder.
There will be no change in the output when the code is executed because Node.js ignores the debugger keyword by default.
Most good code editors have some degree of support for debugging TypeScript and JavaScript code. In this section, I show you how to perform debugging with Visual Studio Code to give you an idea of the process. There may be different steps required if you use another editor, but the basic approach is likely to be similar.
To set up the configuration for debugging, select Add Configuration from the Run menu and select Node.js or Node.js (legacy) from the list of environments when prompted, as shown in Figure 6-1.
If selecting the Add Configuration menu doesn’t work, try selecting Start Debugging instead.

Selecting the debugger environment

Debugging an application using Visual Studio Code
The state of the application is displayed in the sidebar, showing the variables that are set at the point that execution was halted. A standard set of debugging features is available, including setting watches, stepping into and over statements, and resuming execution. The Debug Console window allows JavaScript statements to be executed in the context of the application so that entering a variable name and pressing Return, for example, will return the value assigned to that variable.
Node.js provides a basic integrated debugger. Open a new command prompt and use it to run the command shown in Listing 6-5 in the tools folder.
There are no hyphens before the inspect argument in Listing 6-5. Using hyphens enables the remote debugger described in the following section.
Type help and press Return to see a list of commands. Press Control+C twice to end the debugging session and return to the regular command prompt.

Configuring Chrome for remote Node.js debugging

Discovering the Node.js runtime

Debugging with the Chrome developer tools
A linter is a tool that checks code files using a set of rules that describe problems that cause confusion, produce unexpected results, or reduce the readability of the code. The standard linter package for TypeScript is typescript-eslint, which adapts the popular JavaScript linter package eslint to work with TypeScript. To add the linter to the project, use a command prompt to run the commands shown in Listing 6-9 in the tools folder.
The standard TypeScript linter used to be TSLint, but this has been deprecated in favor of the typescript-eslint package.
Name | Description |
|---|---|
eslint:recommended | This is the set of rules suggested by the ESLint development team and is intended for general JavaScript development. |
@typescript-eslint/eslint-recommended | This set overrides the recommended set to disable rules that are not required for linting TypeScript code. |
@typescript-eslint/recommended | This set contains additional rules that are specific to TypeScript code. |
The linter locates the TypeScript code files and checks them for compliance with the rules specified in the configuration file. The code in the example project breaks two of the linter’s rules: the prefer-const rule requires the const keyword to be used in place of let when the value assigned to a variable isn’t changed, and the no-debugger rule prevents the debugger keyword from being used.
The problem is that the value of a linting rule is often a matter of personal style and preference, and even when the rule is useful, it isn’t always helpful in every situation. Linting works best when you only get warnings that you want to address. If you receive a list of warnings that you don’t care about, then there is a good chance you won’t pay attention when something important is reported.
The rules configuration section is populated with the names of the rules and a value of 1 or 0 to enable or disable the rules. By setting a value of 0 for the prefer-const rule, I have told the linter to ignore my use of the let keyword when const would be a better choice.
Some rules are useful in a project but disabled for specific files or statements. This is the category into which the no-debugger rule falls. As a general principle, the debugger keyword should not be left in code files in case it causes problems during code execution. However, when investigating a problem, debugger is a useful way to reliably take control of the execution of the application, as demonstrated earlier in this chapter.
The comment in Listing 6-13 tells the linter not to apply the no-debugger rule to the highlighted statement.
Rules can be disabled for all the statements that follow a block comment (one that starts with /* and ends with */) that starts with eslint-disable. You can disable all linting rules by using the eslint-disable or eslint-disable-line comment without any rule names.
Linters can be a powerful tool for good, especially in a development team with mixed levels of skill and experience. Linters can detect common problems and subtle errors that lead to unexpected behavior or long-term maintenance issues. I like this kind of linting, and I like to run my code through the linting process after I have completed a major application feature or before I commit my code into version control.
But linters can also be a tool of division and strife. In addition to detecting coding errors, linters can be used to enforce rules about indentation, brace placement, the use of semicolons and spaces, and dozens of other style issues. Most developers have style preferences that they adhere to and believe that everyone else should, too. I certainly do: I like four spaces for indentation, and I like opening braces to be on the same line as the expression they relate to. I know that these are part of the “one true way” of writing code, and the fact that other programmers prefer two spaces, for example, has been a source of quiet amazement to me since I first started writing code.
Linters allow people with strong views about formatting to enforce them on others, generally under the banner of being “opinionated.” The logic is that developers spend too much time arguing about different coding styles, and everyone is better off being forced to write in the same way. My experience is that developers will just find something else to argue about and that forcing a code style is often just an excuse to make one person’s preferences mandatory for an entire development team.
I often help readers when they can’t get book examples working (my email address is adam@adam-freeman.com if you need help), and I see all sorts of coding styles every week. I know, deep in my heart, that anyone who doesn’t follow my personal coding preferences is just plain wrong. But rather than forcing them to code my way, I get my code editor to reformat the code, which is a feature that every capable editor provides.
My advice is to use linting sparingly and focus on the issues that will cause real problems. Leave formatting decisions to the individuals and rely on code editor reformatting when you need to read code written by a team member who has different preferences.
Some unit test frameworks provide support for TypeScript, although that isn’t as useful as it may sound. Supporting TypeScript for unit testing means allowing tests to be defined in TypeScript files and, sometimes, automatically compiling the TypeScript code before it is tested. Unit tests are performed by executing small parts of an application, and that can be done only with JavaScript since the JavaScript runtime environments have no knowledge of TypeScript features. The result is that unit testing cannot be used to test TypeScript features, which are solely enforced by the TypeScript compiler.
The jest package contains the testing framework. The ts-jest package is a plugin to the Jest framework and is responsible for compiling TypeScript files before tests are applied.
Unit testing is a contentious topic. This section assumes you do want to do unit testing and shows you how to set up the tools and apply them to TypeScript. It isn’t an introduction to unit testing, and I make no effort to persuade skeptical readers that unit testing is worthwhile. If would like an introduction to unit testing, then there is a good article here: https://en.wikipedia.org/wiki/Unit_testing.
I like unit testing, and I use it in my own projects—but not all of them and not as consistently as you might expect. I tend to focus on writing unit tests for features and functions that I know will be hard to write and are likely to be the source of bugs in deployment. In these situations, unit testing helps structure my thoughts about how to best implement what I need. I find that just thinking about what I need to test helps produce ideas about potential problems, and that’s before I start dealing with actual bugs and defects.
That said, unit testing is a tool and not a religion, and only you know how much testing you require. If you don’t find unit testing useful or if you have a different methodology that suits you better, then don’t feel you need to unit test just because it is fashionable. (However, if you don’t have a better methodology and you are not testing at all, then you are probably letting users find your bugs, which is rarely ideal.)
The roots setting is used to specify the location of the code files and unit tests. The transform property is used to tell Jest that files with the ts and tsx file extension should be processed with the ts-jest package, which ensures that changes to the code are reflected in tests without needing to explicitly start the compiler. (TSX files are described in Chapter 14.)
Name | Description |
|---|---|
toBe(value) | This method asserts that a result is the same as the specified value (but need not be the same object). |
toEqual(object) | This method asserts that a result is the same object as the specified value. |
toMatch(regexp) | This method asserts that a result matches the specified regular expression. |
toBeDefined() | This method asserts that the result has been defined. |
toBeUndefined() | This method asserts that the result has not been defined. |
toBeNull() | This method asserts that the result is null. |
toBeTruthy() | This method asserts that the result is truthy. |
toBeFalsy() | This method asserts that the result is falsy. |
toContain(substring) | This method asserts that the result contains the specified substring. |
toBeLessThan(value) | This method asserts that the result is less than the specified value. |
toBeGreaterThan(value) | This method asserts that the result is more than the specified value. |
In this chapter, I introduced three tools that are often used to support TypeScript development. The Node.js debugger is a useful way to inspect the state of applications as they are being executed, the linter helps avoid common coding errors that are not detected by the compiler but that cause problems nonetheless, and the unit test framework is used to confirm that code behaves as expected. In the next chapter, I start describing TypeScript features in depth, starting with static type checking.
In this chapter, I introduce the key TypeScript features for working with data types. The features I describe in this chapter are the foundations for working with TypeScript, and they are the building blocks for the advanced features described in later chapters.
I start by showing how TypeScript’s types differ from pure JavaScript’s types. I demonstrate that the TypeScript compiler is able to infer data types from code, and then I introduce features that provide precise control over data types, either by giving the TypeScript compiler information about how sections of code are expected to behave or by changing the way that the compiler is configured. Table 7-1 summarizes the chapter.
For quick reference, Table 7-2 lists the TypeScript compiler options used in this chapter.
To create the example project for this chapter, create a folder called types in a convenient location. Open a new command prompt, navigate to the types folder, and run the command shown in Listing 7-1 to initialize the folder for use with NPM.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
Problem | Solution | Listing |
|---|---|---|
Specify a type | Use a type annotation or allow the compiler to infer a type | |
Inspect the types that the compiler infers | Enable the declarations compiler option and inspect the compiled code | |
Allow any type to be used | Specify the any or unknown types | |
Prevent the compiler from inferring the any type | Enable the noImplicityAny compiler option | |
Combine types | Use a type union | |
Override the type expected by the compiler | Use a type assertion | |
Test for a primitive value type | Use the typeof operator as a type guard | |
Prevent null or undefined from being accepted as values of other types | Enable the strictNullChecks compiler option | |
Override the compiler to remove null values from a union | Use a non-null assertion or use a type guard | |
Allow a variable to be used when it has not been assigned a value | Use the definite assignment assertion |
Name | Description |
|---|---|
declaration | This option produces type declaration files when enabled, which can be useful in understanding how types have been inferred. These files are described in more detail in Chapter 14. |
noImplicitAny | This option prevents the implicit use of the any type, which the compiler uses when it can’t infer a more specific type. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
strictNullChecks | This option prevents null and undefined from being accepted as values for other types. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
The first statement in Listing 7-8 defines the variable without assigning a value, which means that its type is undefined. A variable whose type is undefined will always have a value of undefined, which can be seen in the output.
Name | Description |
|---|---|
number | This type is used to represent numeric values. |
string | This type is used to represent text data. |
boolean | This type can have true and false values. |
symbol | This type is used to represent unique constant values, such as keys in collections. |
null | This type can be assigned only the value null and is used to indicate a nonexistent or invalid reference. |
undefined | This type is used when a variable has been defined but has not been assigned a value. |
object | This type is used to represent compound values, formed from individual properties and values. |
From a JavaScript perspective, there is nothing wrong with this example. Function parameters can receive values of any type, and JavaScript has handled each type exactly as it should. But the calculateTax function has been written with the assumption that it will only receive number values, which is why only the first result makes sense. (The second result, NaN, means not a number, and the third result is obtained by coercing true to the number value 1 and using that in the calculation—see Chapter 4 for details of JavaScript type coercion.)
It is easy to understand the function’s assumption about its parameter type when you can see the code next to the statements that use it, but it’s much harder when the function has been written by another programmer and is deep inside a complex project or package.

Applying type annotations
The type annotation on the function parameter tells the compiler that the function accepts only number values. The annotation that follows the function signature indicates the result type and tells the compiler that the function returns only number values.
You may also see warnings in your code editor if it has good support for TypeScript. I use Visual Studio Code for TypeScript development, and it highlights problems directly in the editor window.
The TypeScript compiler can infer the type of the price variable based on the literal value that it is assigned when it is defined. The compiler knows that 100 is a number value and treats the price variable as though it has been defined with a number type annotation, which means that it is an acceptable value to use as an argument to the calculateTax function.
The compiler is also able to infer the result of the calculateTax function because it knows that only number parameters will be accepted, that 1.2 is a number value, and that the result of the multiplication operator on two number values is a number.
The result from the function is assigned to the taxAmount variable, which the compiler is also able to infer as a number. Finally, the compiler knows the type produced by the division operator on two number values and can infer the type of the halfShare variable, too.
This is legal JavaScript and will be dealt with by type coercion, as described in Chapter 3. In this case, the string value will be converted to a number, and the outcome will be either the division of two number values or NaN if the string value cannot be converted.
The TypeScript compiler doesn’t prevent the use of the JavaScript type features, but it does generate errors when it sees statements that can lead to problems.
The purpose of the declare keyword—and the file itself—is explained in Chapter 14, but this file reveals the types that the compiler has inferred for the statements in Listing 7-15, showing that the return types for the calculateTax function and the taxAmount variable are string. When you get a compiler error, looking at the files generated when the declaration setting is true can be helpful, especially if you can’t see any obvious cause.
The compiler trusts that the any value can be treated as a number, which means a type mismatch occurs at runtime. The any type allows full use of the JavaScript type features, which can be useful but can lead to unexpected results when types are coerced automatically at runtime.
TypeScript also provides the unknown type to provide deliberate access to the dynamic type features while restricting accidental use, as described in the “Using the Unknown Type” section.
Explicitly using any provides an escape-hatch from type checking, which can be useful when applied cautiously. Allowing the compiler to use any implicitly creates gaps in type checking that you may not even notice and that can undermine the benefit of using TypeScript.
The compiler will now display this warning when it cannot infer a more specific type, although this doesn’t prevent the explicit use of any.

Defining a type union

The effect of a type union

Asserting a type
No type conversion is performed by a type assertion, which only tells the compiler what type it should apply to a value for the purposes of type checking.
The result produced by the function has been described to the compiler as the string | number union and asserted as a boolean. But when the code is executed, the function produces a number, whose value is written to the console.
The problem with this syntax is that it cannot be used in TSX files, which combine HTML elements with TypeScript code and are commonly used in React development, as described in Chapter 19. For this reason, the as keyword is the preferred way to assert types.
To test a type, the typeof keyword is applied to a value, producing a string that can be compared to the names of the primitive JavaScript types, such as number and boolean.
The typeof keyword can be used only with the JavaScript primitive types. A different approach is required to differentiate between objects, as described in Chapter 3 and Chapter 10.
Something has gone wrong if execution reaches the default clause of the switch statement, and TypeScript provides the never type to ensure you can’t accidentally use a value once type guards have been used to exhaustively narrow a value to all of its possible types.
There is a hole in the TypeScript static type system: the JavaScript null and undefined types. The null type can be assigned only the null value and is used to represent something that doesn’t exist or is invalid. The undefined type can be assigned only the undefined value and is used when a variable has been defined but not yet assigned a value.
Under normal circumstances, the compiler will report an error if a value of one type is assigned to a variable of a different type, but the compiler remains silent because it allows null and undefined to be treated as values for all types.
In addition to type inconsistencies, nullable values can lead to runtime errors that are difficult to detect during development and often encountered by users. In Listing 7-31, for example, there is no easy way for consumers of the calculateTax function to know that a null value may be returned and to understand when that might happen. It is easy to see the null value and the reasons for its use in the example but much harder to do the same thing in a real project or in a third-party package.
The configuration change tells the compiler to produce an error when null or undefined values are assigned to another type. In this example, the error occurs because the null value returned by the calculateTax function isn’t one of the types in the union that describes the function’s result.
Remember that unions present the intersection of the API of each individual type. The null and undefined values don’t present any properties or methods, which means that values for nullable type unions can’t be used directly, even if the non-null types have an intersection of useful properties or methods (of which there are examples in later chapters). A non-null assertion tells the compiler that a value isn’t null, which removes null from the type union and allows the intersection of the other types to be used, as shown in Listing 7-34.
A non-null assertion should be used only when you know that a null value cannot occur. A runtime error will be caused if you apply the assertion and a null value does occur. A safer approach is to use a type guard, as described in the next section.

Asserting a non-null value
If the strictNullChecks option is enabled, the compiler will report an error if a variable is used before it is assigned a value. This is a helpful feature, but there can be times where a value is assigned in a way that isn’t visible to the compiler, as shown in Listing 7-36.
I use the built-in JavaScript eval function in Listing 7-36 to execute a string as a code statement. The eval function is considered dangerous and should not be used in real projects.
In this chapter, I explained how TypeScript can be used to restrict the JavaScript type system by performing type checking. I demonstrated how type annotations can be used to specify the types that can be used and how the compiler can infer types from code statements. I explained the use of the any, unknown, and never types; type unions; and guards that restrict the range of types. In the next chapter, I explain how TypeScript deals with functions in more depth.
Problem | Solution | Listing |
|---|---|---|
Allow a function to be called with fewer arguments than parameters | Define optional parameters or define parameters with default values | |
Allow a function to be called with more arguments than parameters | Use a rest parameter | |
Restrict the types that can be used for parameter values and results | Apply type annotations to parameters or function signatures | |
Prevent null values from being used as function arguments | Enable the strictNullChecks compiler option | |
Ensure that all function code paths return a result | Enable the noImplicitReturns compiler option | |
Describe the relationship between the types of a function’s parameters and its result | Overload the function’s types | |
Describe the effect of an assert function | Use the assert keyword |
Name | Description |
|---|---|
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
declaration | This option produces type declaration files when enabled, which can be useful in understanding how types have been inferred. These files are described in more detail in Chapter 14. |
strictNullChecks | This option prevents null and undefined from being accepted as values for other types. |
noImplicitReturns | This option requires all paths in a function to return a result. |
noUnusedParameters | This option causes the compiler to produce a warning if a function defines parameters that are not used. |
In this chapter, I continue to use the types project created in Chapter 7. To prepare for this chapter, replace the contents of the index.ts file in the src folder with the code shown in Listing 8-1.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
Chapter 7 demonstrated how TypeScript features like type annotations can be applied to functions. In the sections that follow, I revisit these features and describe the other ways that TypeScript enhances functions.
Many languages support function overloading, which allows multiple functions to be defined with the same name as long as they have different numbers of parameters or if the parameters have different types. If you are used to this style of programming, the code in Listing 8-4 looks perfectly normal, and you will assume the second calculateTax function builds on the first calculateTax function to apply a discount.
JavaScript doesn’t support function overloading, and when you define two functions with the same name, the second function replaces the first, regardless of the function’s parameters. The number of arguments used to call a function is not important in JavaScript—if there are more parameters than arguments, then the extra parameters are undefined. If there are more arguments than parameters, the function can either ignore them or use the special arguments value, which provides access to all the arguments used to invoke the function. If the code in Listing 8-4 were executed, the first calculateTax function would be ignored, and the second function would be invoked, but without a value for the second parameter. When the function is executed, it would invoke itself repeatedly, until the call stack becomes exhausted and an error is produced.
The compiler insists on matching arguments to parameters to make the expectations in the code explicit, just as for the features described in Chapter 7. When you examine a set of parameters, you can’t easily determine how the function will behave if some of them don’t receive values. And when a function is invoked with a different number of arguments, it is difficult to determine whether this is intentional or an error. TypeScript tackles both of these problems by requiring arguments that correspond to all parameters unless the function indicates that it can be more flexible using the features described in the following sections.
If the noUnusedParameters option is enabled, the compiler will warn you if a function defines parameters that it doesn’t use.
Optional parameters are defined by placing a question mark after the parameter name, as illustrated in Figure 8-1.
Optional parameters must be defined after the required parameters. This means that I cannot reverse the order of the amount and discount parameters in Listing 8-7, for example, because amount is required and discount is optional.

Defining an optional parameter
The discount parameter is used in the same way as the required parameter, and the only change is that the function must be able to deal with the possibility of an undefined value.

Defining a default parameter value
Using a default value means that the code in the function doesn’t have to check for undefined values and means that the fallback value can be changed in a single location and take effect throughout the function.
Parameters with default values are still optional parameters, even though no question mark is used, and must be defined after the function’s required parameters.

Defining a rest parameter
Any arguments for which there are no corresponding parameters are assigned to the rest parameter, which is an array. The array will always be initialized and will contain no items if there were no extra arguments. The addition of the rest parameter means that the calculateTax function can be called with one or more arguments: the first argument is assigned to the amount parameter, the section argument (if there is one) is assigned to the discount parameter, and any other arguments are added to the extraFees parameter array.
Type annotations for optional parameters are applied after the question mark, like this: discount?: number.
The TypeScript compiler will try to infer the result type from the code in the function and will automatically use type unions if a function can return multiple types. The easiest way to see what type the compiler infers for a function result is to enable the generation of type declaration files, using the declaration setting, which was enabled in Listing 8-2. These files are used to provide type information when a package is used in another TypeScript project, and I describe their use in Chapter 14.
The highlighted part of the type information for the calculateTax function shows the type inferred by the compiler for the function’s result.
JavaScript has an unusually relaxed approach to function results, such that a function will return undefined for any path through the function’s code that doesn’t reach a statement with the return keyword, which is known as the implicit return feature.
The type guard used to filter out null values means that there is a path through the function’s code that doesn’t reach a return statement and so the function will return a number if the amount parameter isn’t null and will return undefined if the amount parameter is null. The strictNullChecks compiler option was enabled in Listing 8-14, so the compiler has inferred the result type to be number | undefined.
The writeValue function doesn’t return a result and has been annotated with the void type. Using void ensures that the compiler will warn you if the result keyword is used or if the function is used to assign a value.
The never type can be used as the result type for functions that will never complete, such as functions that will always throw an exception, for example.
The type annotation in Listing 8-19 describes the types that the calculateTax function will accept, telling users that the function will accept either a number or null and will return a number or null. The information provided by the type unions is correct but does not fully describe the situation. What’s missing is the relationship between the parameter and result types: the function will always return a number result if the amount parameter is a number parameter and will always return null if amount is null. The missing details in the function’s types mean that the user of the function has to use a type guard on the result to remove null values, even though the value 100 is a number and will always produce a number result.
To describe the relationships between the types used by a function, TypeScript supports type overloads, as shown in Listing 8-20.
This is not the function overloading supported by languages such as C# and Java. Only the type information is overloaded by this feature for the purposes of type checking. As Listing 8-20 shows, there is only one implementation of the function, which is still responsible for dealing with all the types used in the overloads.

A function type overload
You can also express the relationship between parameters and results using the conditional types feature, which is described in Chapter 13.
The check function defines a boolean parameter and throws an error if it is false. This is the basic pattern of an assert function.
The calculateTax function accepts a number | null argument and uses the check function to narrow the type so that null values cause errors and so number values are used to produce a result.

Denoting an assert function
The TypeScript compiler can take the effect of the check function into account and knows that the calculateTax function narrows the type of amount parameter to exclude null values.
In this example, the assets keyword is followed by val is number, which tells the TypeScript compiler that the effect of the checkNumber function is to ensure that the val parameter is a number value.
In this chapter, I described the features that TypeScript provides for functions. I explained how duplicate function definitions are prevented, showed you the different ways to describe function parameters and results, and described how to override function types to create more specific mappings between parameter types and the results they produce. In the next chapter, I describe how TypeScript addresses simple data structures.
Problem | Solution | Listing |
|---|---|---|
Restrict the range of types that an array can contain | Apply a type annotation or allow the compiler to infer the types from the value used to initialize the array | |
Define fixed-length arrays with specified types for each value | Use a tuple | |
Define variable-length arrays with specified types for each value | Use a tuple with a rest element | |
Refer to a collection of related values through a single name | Use an enum | |
Define a type that can be assigned only specific values | Use a literal value type | |
Avoid duplication when describing a complex type | Use a type alias |
Name | Description |
|---|---|
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
declaration | This option produces type declaration files when enabled, which can be useful in understanding how types have been inferred. These files are described in more detail in Chapter 14. |
strictNullChecks | This option prevents null and undefined from being accepted as values for other types. |
In this chapter, I continue to use the types project created in Chapter 7. To prepare for this chapter, replace the contents of the index.ts file in the src folder with the code shown in Listing 9-1.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.

An array type annotation
TypeScript uses an annotation to restrict the operations that can be performed on the array to the specified type: one of the arrays in the listing is restricted to number values and the other to string values. In Listing 9-5, I have used the JavaScript forEach method on the arrays, and you can see that the function I used to process the array values is typed to match the array types.
You can use parentheses when describing an array that contains multiple types, such as when using a type union (described in Chapter 8) or a type intersection (described in Chapter 10). For example, an array whose elements can be number or string values can be annotated as (number | string)[], where the parentheses around the type union prevent the compiler from assuming that the union is between a single number or an array of strings.
The problem with this syntax is that it cannot be used in TSX files, which combine HTML elements with TypeScript code, as described in Chapter 15. For this reason, the square bracket syntax is the preferred way to assert array types.
The compiler can determine the array types based on the set of values that are assigned when the arrays are initialized, and it uses the inferred types to follow through to the forEach method.
The compiler is skilled at inferring types, but if you don’t get the results you expect, you can inspect the files that the compiler emits when the declaration option is enabled. This option generates type declaration files, which are used to provide type information when a package is used in another TypeScript project and which are described in detail in Chapter 14.
I explain the declare keyword in Chapter 14. For the moment, it is enough to see that the compiler has correctly inferred the array types from the initial values.
The change in the array type causes the error message because the function passed to the forEach method treats the values as number when they are now part of the string | number union. It is easy to see the cause of the problem in a simple example, but it becomes more difficult when the initial values for the array come from different parts of an application. I find it more useful to declare the array type explicitly, which means that problems like the one in Listing 9-7 produce a compiler error that highlights my error in trying to add a string to a number array.
The effect of allowing the compiler to infer the type of the empty array is to create a gap in the type checking process. The code works because the JavaScript multiplication operator coerces string values to number values automatically. This can be useful behavior, but it is likely to be used accidentally, and it is for this reason that you should use explicit types.
Inferring the never type ensures that the array doesn’t escape the type checking process and the code won’t compile until a type is asserted for the array or the array is initialized using values that allow the compiler to infer a less restrictive type.

Defining a tuple
The type of the hat tuple in Listing 9-10 is [string, number], which defines a tuple with two elements, where the first element is a string and the second value is a number. The elements in the tuple are accessed using the array index syntax so that the first element of the hat tuple is hat[0], for example.
Tuples must be defined with type annotations; otherwise, the compiler will assume that a regular array with a type that is the union of each value used during initialization. Without the type annotation shown in Figure 9-2, for example, the compiler would assume that the type of the value assigned to the hat variable is [string | number], which would denote a variable-length array in which every element can be either a string or number value.
The hat tuple is destructured, and its values are assigned to hatname and hatprice variables, which are written to the console. There is no change in the output in this example; only the way the tuples values are accessed has changed.
The tuple type in Listing 9-14 has an optional number element. (A tuple can have multiple optional elements, but they must be the last elements defined by the tuple type.)
The type of the optional element is a union of the specified type and undefined so that in the example, the type is number | undefined. The value of the element will be undefined if no value has been provided, and it is the responsibility of the code that processes the tuple to narrow the type to exclude undefined values.
This is not a feature that I like because the variable lengths introduced by the rest elements undermine the fixed structure that makes tuples useful. The only time I use this feature is when describing JavaScript code, as described in Chapter 14.

Defining an enum
The compiler consults the previous value only when it generates a number value and doesn’t check to see whether the value has already been used, which can lead to duplicate values in an enum.
The default implementation of enums represents each value with a number, but the compiler can also use string values for enums, as shown in Listing 9-21.
An enum can contain both string and number values, although this is not a feature that is widely used.
Enums can be useful, but there are some limitations because they are a feature that is implemented entirely by the TypeScript compiler and then translated into pure JavaScript.
The compiler doesn’t prevent the assignment of a number to a variable whose type is an enum when the number doesn’t correspond to one of the enum values, which is why the output shown for Listing 9-21 contains undefined, as the lookup fails to find a corresponding Product name for the number value. The same issue arises if a function uses an enum as its result type because the compiler will allow it to return any number value.
This isn’t a problem with string enums, which are implemented differently behind the scenes and can be assigned values only from the enum.
The TypeScript compiler creates an object that provides the implementation for an enum. In some applications, the performance impact of using the object can be a problem, and a different approach can be used instead.
This is an advanced feature that is rarely required in most projects.
You don’t have to understand how this code works. What’s important is that a Product object is created and that it is used when the value is assigned to the productValue variable.
To prevent the compiler from using an object to implement an enum, the const keyword can be used when the enum is defined in the TypeScript file, as shown in Listing 9-24.
Const enums are more restrictive than regular enums, and all of the values must be assigned constant expressions. The simplest way to do this is to allow the compiler to assign values or to explicitly assign values yourself.
The comment is included by the compiler to indicate the relationship between the number value and the enum. The object that previously represented the enum is no longer included in the compiled code.
The object used to represent a normal enum is responsible for providing the lookup feature and isn’t available for const enums.
There is a compiler option named preserveConstEnums that tells the compiler to generate the object even for const enums. This feature is only for debugging, and it doesn’t restore the lookup feature.

A literal value type
Literal value types can be used in type unions with regular types, creating combinations that permit specific values of one type with any legal values for another. For example, the type union string | true | 3 can be assigned any string value, the true boolean value, and the number value 3.

Defining a type alias
In this chapter, I explained how TypeScript can be used with arrays and introduced the tuples and enums features, which are implemented by the TypeScript compiler. I also showed you how to define literal value types and how to use aliases to describe types consistently. In the next chapter, I describe the features that TypeScript provides for working with objects.
Problem | Solution | Listing |
|---|---|---|
Describe an object to the TypeScript compiler | Use a shape type | |
Describe irregular shape types | Use optional properties | |
Use the same shape to describe multiple objects | Use a type alias | |
Prevent compiler errors when a type contains a superset of the properties in a shape | Enable the suppressExcessPropertyErrors compiler option | |
Combine shape types | Use type unions or intersections | |
Type guard for object types | Check the properties defined by an object using the in keyword | |
Reuse a type guard | Define a predicate function |
Name | Description |
|---|---|
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
declaration | This option produces type declaration files when enabled, which can be useful in understanding how types have been inferred. These files are described in more detail in Chapter 14. |
strictNullChecks | This option prevents null and undefined from being accepted as values for other types. |
suppressExcessPropertyErrors | This option prevents the compiler from generating errors for objects that define properties not in a specified shape. |
The compiler configuration includes the declaration setting, which means that the compiler will create type declaration files alongside the JavaScript files. The real purpose of declaration files is explained in Chapter 14, but they will be used in this chapter to explain how the compiler deals with data types.
Open a new command prompt, navigate to the types folder, and run the command shown in Listing 10-3 to start the TypeScript compiler so that it automatically executes code after it has been compiled.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
JavaScript objects are collections of properties that can be created using the literal syntax, constructor functions, or classes. Regardless of how they are created, objects can be altered once they have been created, adding or removing properties and receiving values of different types. To provide type features for objects, TypeScript focuses on an object’s “shape,” which is the combination of its property names and types.
I have formatted the contents of the declaration file to make it easier to see how the compiler has identified the type of each object using its shape. When the objects are placed into an array, the compiler uses the shape of the objects to set the type of the array to match.
Notice that the type for the products array has changed. When objects of different shapes are used together, such as in an array, the compiler creates a type that has the common properties of the objects it contains because they are the only properties that are safe to work with. In the example, the only property common to all the objects in the array is the string property name, which is why the compiler reports an error for the statement that tries to read the price property.

An object shape type
Notice that type annotations are not required to indicate that individual objects have a specific shape. The TypeScript compiler automatically determines whether an object conforms to a shape by inspecting its properties and their values.

An optional property in a shape type
The hat and gloves objects don’t define the optional waterproof property, so the value received in the forEach function is undefined. The umbrella object does define this property, and its value is displayed.

A method in a shape type
The method included in the shape type in Listing 10-8 specifies a method called hasFeature that has one parameter, which must be a value from the Feature enum (also defined in Listing 10-8) and which returns a boolean result.
Methods in shape types don’t have to be optional, but when they are, as in Listing 10-8, the question mark comes after the method name and before the parentheses that denote the start of the parameter types.
As with regular properties, you must ensure that a method is implemented before it is invoked.
The compiler treats the mirrorShades and darkShades objects differently, even though they have the same shape. The compiler reports errors when object literals with type annotations define additional properties, because this is likely to be a mistake. In the case of the example, the darkShades object has a Product type annotation. The finish property isn’t part of the Product shape and is known as an excess property, which the compiler reports as an error. Excess properties do not cause errors when an object is defined without a type annotation, which means the darkShades object can be used as a Product.
The previous section demonstrated how unions of shape types can be useful in their own right, but type guards are still required to get to a specific type to access all of the features it defines.
The shape type feature is provided entirely by TypeScript, and all objects have the type object as far as JavaScript is concerned, with the result that the typeof keyword isn’t useful for determining whether an object conforms to the Product and Person shapes.
The goal is to be able to determine each object in the array conforms to the Product shape or the Person shape. We know these are the only types that the array can contain because its type annotation is (Product | Person)[].

Using the in keyword
It is important to create type guard tests that definitively and accurately differentiate between types. If the compiler gives you unexpected errors when you have used a type guard, then the likely cause is an inaccurate test.
This test checks for id and name properties, but these are defined by both the Person and Product types, and the test doesn’t give the compiler enough information to infer a type. The type inferred in the if block is the Product | Person union, which means the use of the city property will generate an error. The type inferred in the else block is never, since all the possible types have already been inferred, and the compiler will generate errors for the use of the name and price properties.
The test will match objects that define a price property, which means that the type inferred in the if block will be Product, as intended (notice that the statements in the code blocks are reversed in this example). The problem is that objects can still match the Product shape if they don’t have a price property, which means the type inferred in the else block is Product | Person and the compiler will report an error for the use of the city property.
Writing effective tests for types can require careful thought and thorough testing, although the process becomes easier with experience.

An object type guard function
The result of the function, which is a type predicate, tells the compiler which of the function’s parameters is being tested and the type that the function checks for. In Listing 10-18, the isPerson function tests its testObj parameter for the Person type. If the result of the function is true, then the TypeScript compiler will treat the object as the specified type.
Using a function for type guarding can be more flexible because the parameter type is any, allowing properties to be tested for without having to use string literals and the in keyword.
The are no restrictions on the name of the type guard function, but the convention is to prefix the guarded type with is, such that a function that tests for the Person type is named isPerson and a function that tests for the Product type is named isProduct.

Defining an intersection type

The effect of a type intersection
In Listing 10-19, the intersection between Person and Employee types has the effect that the dataItems array can contain only objects that define id, name, city, company, and dept properties.
I used type annotations in Listing 10-20 to make the purpose of the code easier to understand, but the code would work without them. The TypeScript compiler is adept at understanding the effect of code statements and can understand the effect of this statement is to create objects that conform to the shape of the type intersection.
It may seem obvious that an intersection type is compatible with each of its constituents, but it has an important effect when the types in the intersection define properties with the same name: the type of the property in the intersection is an intersection of the individual property types. That sentence is hard to make sense of, so the sections that follow provide a more useful explanation.

Merging properties with the same type
There are no issues to deal with in this situation because any value assigned to the id property will be a string and will conform to the requirements of the object and intersection types.

Merging properties with different types
The intersection of number and string is an impossible type. There is no way to work around this problem for primitive types, and the only solution is to adjust the types used in the intersection so that shape types are used instead of primitives, as shown in Listing 10-24.
It might seem odd that the TypeScript compiler allows impossible types to be defined, but the reason is that some of the advanced TypeScript features, described in later chapters, make it difficult for the compiler to deal with all situations consistently, and the Microsoft development team has chosen simplicity over exhaustively checking for every impossible type.

Merging properties with shape types
The intersection of an object with a phone property and an object with a name property is an object with phone and name properties, which makes it possible to assign contact values that conform to the Person and Employee types and their intersection.

Merging methods
The first statement shows the type of the intersected method, and the other statements show the type returned when string and number arguments are used. (I explain the intended purpose of the index.d.ts file in Chapter 14, but taking advantage of this feature to see the types that the compiler is working with is often useful.)
In this chapter, I describe the way that TypeScript uses an object’s shape to perform type checking. I explained how shapes are compared, how shapes can be used for aliases, and how shapes are combined into unions and intersections. In the next chapter, I explain how the shape features are used to provide type support for classes.
Problem | Solution | Listing |
|---|---|---|
Create objects consistently | Use a constructor function or define a class | |
Prevent access to properties and methods | Use the TypeScript access control keywords or JavaScript private fields | |
Prevent properties from being modified | Use the readonly keyword | |
Receive a constructor parameter and create an instance property in a single step | Use the concise constructor syntax | |
Define partial common functionality that will be inherited by subclasses | Define an abstract class | |
Define a shape that classes can implement | Define an interface | |
Define a property dynamically | Use an index signature |
Name | Description |
|---|---|
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
declaration | This option produces type declaration files when enabled, which can be useful in understanding how types have been inferred. These files are described in more detail in Chapter 14. |
noUncheckedIndexedAccess | This option does not allow properties accessed via an index signature to be accessed until they have been guarded against undefined values. |
The compiler configuration includes the declaration setting, which means that the compiler will create type declaration files alongside the JavaScript files. The intended purpose for declaration files is explained in Chapter 14, but they will be used in this chapter to explain how the compiler deals with data types.
Open a new command prompt, navigate to the types folder, and run the command shown in Listing 11-3 to start the TypeScript compiler so that it automatically executes code after it has been compiled.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
TypeScript treats the Employee constructor function like any other function and looks at its parameter and result types to describe its shape. When the Employee function is used with the new keyword, the compiler uses the any type for the object assigned to the salesEmployee variable. The result is a series of errors as the compiler struggles to make sense of the way the constructor function is used.
The TypeScript compiler may not understand the significance of the constructor function, but it can match the objects it creates by shape. The listing adds a shape type that corresponds to those created by the constructor function, including the method that is accessed through the prototype. For convenience, I have given the shape type an alias that matches the name of the constructor function, but that is optional because the compiler keeps track of variable names and type names separately.
The syntax for a TypeScript class requires the declaration of instance properties and their types. This leads to more verbose classes—although I demonstrate a feature that addresses this shortly—but it has the advantage of allowing the constructor parameter types to be different from the types of the instance properties to which they are assigned. Objects are created from classes using the standard new keyword, and the compiler understands the use of the instanceof keyword for type narrowing when classes are used.
Name | Description |
|---|---|
public | This keyword allows free access to a property or method and is the default if no keyword is used. |
private | This keyword restricts access to the class that defines the property or method it is applied to. |
protected | This keyword restricts access to the class that defines the property or method it is applied to and its subclasses. |

An access control keyword
The only way that the dept property can be accessed is through the writeDept method, as used in Listing 11-8, which is part of the Employee class and allowed by the private keyword.
The access protection features are enforced by the TypeScript compiler and are not part of the JavaScript code that the compiler generates. Do not rely on the private or protected keyword to shield sensitive data because it will be accessible to the rest of the application at runtime.
When the strictPropertyInitialization configuration option is set to true, the TypeScript compiler reports an error if a class defines a property that is not assigned a value, either as it is defined or by the constructor. The strictNullChecks option must also be enabled for this feature to work.
TypeScript supports a JavaScript feature working its way through the standardization process and that is likely to be added to the language specification. This feature is support for private fields, which provides an alternative to the private keyword, as shown in Listing 11-9.
My advice is to use the TypeScript private keyword, at least until private fields become part of the JavaScript specification.

A private field
The key advantage over the TypeScript private keyword is that the # character is not removed during the compilation process, which means that access control is enforced by the JavaScript runtime. Like most TypeScript features, the private keyword is not included in the JavaScript code produced by the compiler, which means that access control is not enforced in the JavaScript code.

A read-only property
The readonly keyword is enforced by the TypeScript compiler and does not affect the JavaScript code that the compiler generates. Do not use this feature to protect sensitive data or operations.

Applying access control keywords to constructor parameters
TypeScript builds on the standard class inheritance features to make them more consistent and familiar, with some useful additions for commonly required tasks and for restricting some of the JavaScript characteristics that can cause problems. Listing 11-13 replaces the Person type alias with a class that provides the same features and uses it as the superclass for Employee.
I have shown multiple classes in the same code file, but a common convention is to separate each class into its own file, which can make a project easier to navigate and understand. You can see more realistic examples in Part 3, where I build a series of web applications.
Caution is required when letting the compiler infer types from classes because it is easy to produce unexpected results by assuming the compiler has insight into the hierarchy of classes.

Defining an abstract class

Defining an abstract method
When a class extends an abstract class, it must implement all the abstract methods. In the example, the abstract Person class defines an abstract method named getSpecificDetails, which must be implemented by the Employee, Customer, and Supplier classes. The Person class also defines a regular method named getDetails, which invokes the abstract method and uses its result.
Interfaces are used to describe the shape of an object, which a class that implements the interface must conform to, as shown in Listing 11-18.
Interfaces have a similar purpose to shape types, described in Chapter 10, and successive versions of TypeScript have eroded the differences between these two features, to the point where they can often be used interchangeably to achieve the same effect, especially when dealing with simple types. Interfaces do have some useful features, however, and they provide a development experience that is more consistent with other languages, such as C#.

Defining an interface

Implementing an interface
Interfaces can be defined in multiple interface declarations, which are merged by the compiler to form a single interface. This is an odd feature—and one that I have yet to find useful in my own projects. The declarations must be made in the same code file, and they must all be exported (defined with the export keyword) or defined locally (defined without the export keyword).
A class can implement multiple interfaces only if there are no overlapping properties with conflicting types. For example, if the Person interface defined a string property named id and if the DogOwner interface defined a number property with the same name, the Customer class would not be able to implement both interfaces because there is no value that could be assigned to its id property that could represent both types.
This fragment of code is based on Listing 11-20 and replaces the Person interface with a shape type that has the same properties. The Employee class uses the implements keyword to declare that it conforms to the Person shape.
In this fragment of code, the Person interface inherits the name property from the NamedObject shape type. Classes that implement the Person interface must define the name property, along with the getDetails method that the interface specifies directly.

Defining optional interface members
The TypeScript compiler only allows values to be assigned to properties that are part of an object’s type, which means that interfaces and classes have to define all the properties that the application requires.
The ProductGroup class receives an array of [string, Product] tuples through its constructor, each of which is used to create a property using the string value as its name and the Product as its value. The compiler will allow the constructor to create the property and give it the any type, unless the noImplicitAny or strict compiler options are enabled, when an error is thrown.

An index signature
In this chapter, I explained the way that TypeScript enhances the JavaScript class feature, providing support for concise constructors, abstract classes, and access control keywords. I also described the interface feature, which is implemented by the compiler and provides an alternative way to describe the shape of objects so that classes can readily conform to them. In the next chapter, I describe the TypeScript support for generic types.
Problem | Solution | Listing |
|---|---|---|
Define a class or function that can safely operate on different types | Define a generic type parameter | |
Resolve a type for a generic type parameter | Use a generic type argument when instantiating the class or invoking the function | |
Extend a generic class | Create a class that passes on, restricts, or fixes the generic type parameter inherited from the superclass | |
Type guard a generic type | Use a type predicate function | |
Describe a generic type without providing an implementation | Define an interface with a generic type parameter |
Name | Description |
|---|---|
declaration | This option produces type declaration files when enabled, which can be useful in understanding how types have been inferred. These files are described in more detail in Chapter 14. |
module | This option specifies the module format, as described in Chapter 5. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
In this chapter, I continue to use the types project created in Chapter 7 and used in every chapter since. To prepare for this chapter, create a file called dataTypes.ts in the src folder, with the contents shown in Listing 12-1.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.

A generic type parameter
A generic type parameter is defined between angle brackets (the < and > characters), and only a name is specified. The convention is to start with the letter T as the name of the type parameter, although you are free to follow any naming scheme that makes sense in your project.

Creating an object with a generic type argument
This statement creates a DataCollection<T> object where the type parameter T will be Person. When an object is created from a generic class, its type incorporates the argument, such as DataCollection<Person>. The compiler enforces the TypeScript type rules using Person wherever it encounters T, which means that only Person objects can be passed to the constructor and the add method and that invoking the getItem method will return a Person object. TypeScript keeps track of the type argument used to create the DataCollection<Person> object, and no type assertions or type narrowing is required.
In Listing 12-7 and Listing 12-8, I commented out the getNames method. By default, any type can be used for a generic type argument, so the compiler treats generic types as any by default, meaning that it won’t let me access the name property on which the getNames method depends without some kind of type narrowing.

A generic type parameter restriction
The change in Listing 12-9 can be thought of as creating two levels of restriction on the DataCollection<T> class: one applied when a new object is created and one that is applied when the object is used.
The first restriction constrains the types that can be used as the generic type argument to create a new DataCollection<Product | Person> object so that only types that can be assigned to Product | Person can be used as the type parameter value. Three types can meet that restriction: Person, Product, and the Person | Product union. These are the only types that can be assigned to the generic type parameter T.
The second restriction applies the value of the generic type parameter when the object is used. When a new object is created with Product as the type parameter, for example, Product is the value of T: the constructor and add methods will only accept Product objects, and the getItem method will only return a Product object. When Person is used as the type parameter, Person is the value of T and becomes the type used by the constructor and methods.
The shape specified in Listing 12-10 tells the compiler that the DataCollection<T> class can be instantiated using any type that has a name property that returns a string. This allows DataCollection objects to be created to deal with Person, Product, and City objects without requiring individual types to be specified.
Generic type parameters can also be constrained using type aliases and interfaces. It is also possible to constrain generic types to those that define a specific constructor shape, which is done with the extends new keywords, which are demonstrated in Chapter 13.
Additional type parameters are separated with commas, just like regular function or method parameters. The DataCollection<T, U> class defines two generic type parameters. The new parameter, named U, is used to define the type of an argument passed to the collate method, which compares the properties on an array of objects and intersections between those T and U objects that have the same property values.
This statement creates a DataCollection<Person, City> object that will store Person objects and compare them to City objects. An array of City objects is passed to the collate method, comparing the values of the city property of the Person objects and the name property of the City objects.
The second type parameter in Listing 12-11 isn’t as flexible as it could be because it requires the data type used by the collate method to be specified when the DataCollection object is created, meaning that’s the only data type that can be used with that method.
The compiler is able to infer the type arguments based on the argument passed to the DataCollection<T> constructor and the first argument passed to the collate method. To check the types inferred by the complier, examine the index.d.ts file in the dist folder, which is created when the declaration option is enabled.
In a project that uses modules, the files created through the declaration option contain only those types that are exported outside a module, which is why I added the export keyword in Listing 12-13.
A generic class can be extended, and the subclass can choose to deal with the generic type parameters in several ways, as described in the following sections.
The type of a generic class includes its type parameters so that the superclass is DataCollection<T>. The type parameter defined by the SearchableCollection<T> class must be compatible with the type parameter of the superclass, so I have used the same shape type to specify types that defined a name property.
Notice I changed the access control keyword on the items property in Listing 12-14 to protected, allowing it to be accessed by subclasses. See Chapter 11 for details of the access control keywords provided by TypeScript.
The type parameter specified by the subclass must be assignable to the type parameter it inherits, meaning that only a more restrictive type can be used. In the example, the Employee | Person union can be assigned to the shape used to restrict the DataCollection<T> type parameter.
Bear in mind that when a union is used to constrain a generic type parameter, the union itself is an acceptable argument for that parameter. This means that the SearchableCollection class in Listing 12-16 can be instantiated with a type parameter of Employee, Product, and Employee | Product. See Chapter 13 for advanced features for restricting type arguments.
Listing 12-17 introduces a filter method that uses the instanceof keyword to select objects of a specific type from the array of data items. A DataCollection<Person | Product> object is created with an array that contains a mix of Person and Product objects, and the new filter method is used to select the Product objects.
Notice that the filter method’s generic type parameter, named V, is defined with the extend keyword, telling the compiler that it can only accept types that can be assigned to the class generic type T, which prevents the compiler from treating V as any.
There is no JavaScript feature that is equivalent to generic types, so they are removed from the TypeScript code during the compilation process, which means that there is no information available at runtime to use generic types with the instanceof keyword.
The Collection<T> interface has a generic type parameter named T, following the same syntax used for class type parameters. The type parameter is used by the add and get methods, and it has been constrained to ensure that only types that have a name property can be used.
An interface with a generic type parameter describes a set of abstract operations but doesn’t specify which types they can be performed on, leaving specific types to be selected by derived interfaces or implementation classes. The code in Listing 12-21 produces no output.
The code in Listing 12-22 does not produce any output.
When a class implements a generic interface, it must implement all the interface properties and methods, but it has some choices about how to deal with type parameters, as described in the following sections. Some of these options are similar to those used when extending generic classes and interfaces.
The ArrayCollection<DataType> class uses the implements keyword to declare that it conforms to the interface. The interface has a generic type parameter, so the ArrayCollection<DataType> class must define a compatible parameter. Since the type parameter for the interface is required to have a name property, so must the type parameter for the class, and I used the same type alias for the interface and the class to ensure consistency.
In this chapter, I introduced generic types and described the problem they solve. I showed you the relationship between generic type parameters and arguments and the different ways that generic types can be restricted or fixed. I explained that generic types can be used with regular classes, abstract classes, and interfaces and showed you how functions and methods can have generic types that are resolved each time they are used. In the next chapter, I describe the advanced generic type features that TypeScript provides.
Problem | Solution | Listing |
|---|---|---|
Use collection classes with type safety | Provide a generic type argument when creating the collection | |
Use iterators with type safety | Use the interfaces that TypeScript provides that support generic type arguments | |
Define a type whose value can only be the name of a property | Use an index type query | |
Transform a type | Use a type mapping | |
Select types programmatically | Use conditional types |
Name | Description |
|---|---|
declaration | This option produces type declaration files when enabled, which can be useful in understanding how types have been inferred. These files are described in more detail in Chapter 14. |
downlevelIteration | This option enables support for iteration when targeting older versions of JavaScript. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
Open a new command prompt, navigate to the types folder, and run the command shown in Listing 13-2 to start the TypeScript compiler so that it automatically executes code after it has been compiled.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
Name | Description |
|---|---|
Map<K, V> | This describes a Map whose key type is K and whose value type is V. |
ReadonlyMap<K, V> | This describes a Map that cannot be modified. |
Set<T> | This describes a Set whose value type is T. |
ReadonlySet<T> | This describes a Set that cannot be modified. |
Name | Description |
|---|---|
Iterator<T> | This interface describes an iterator whose next method returns IteratorResult<T> objects. |
IteratorResult<T> | This interface describes a result produced by an iterator, with done and value properties. |
Iterable<T> | This interface defines an object that has a Symbol.iterator property and that supports iteration directly. |
IterableIterator<T> | This interface combines the Iterator<T> and Iterable<T> interfaces to describe an object that has a Symbol.iterator property and that defines a next method and a result property. |
Iterators were introduced in the JavaScript ES6 standard. If you use iterators in your project and are targeting earlier versions of JavaScript, then you must set the TypeScript downlevelIteration compiler property to true.
The Collection<T> class restricts the types it can accept using a shape type, which ensures that all the objects it deals with have a name property that can be used as the key to store and retrieve objects in the Map.
TypeScript provides a set of related features that allow any property defined by an object to be used as a key while preserving type safety. These features can be difficult to understand, so I show how they work in isolation and then use them to improve the Collection<T> class.

An index type and value
The keyof Product expression returns a literal value type union with the property names defined by the Product class, "name" | "price". The indexed access operator returns the union of the types of those properties, such that Product[keyof Product] is string | number, which is the union of the types of the name and price properties.
The types returned by the indexed access operator are known as lookup types.

The indexed access operator
Name | Description |
|---|---|
T | This is the type of the objects stored in the collection class, which is provided by the first generic type argument, which is Product for the object created in the listing. |
K | This is the key property name, which is restricted to the property names defined by T. The value for this type is provided by the second generic type argument, which is name for the object created in the listing. |
T[K] | This is the type of the key property, which is obtained using the indexed access operator and which is used to specify the key type when creating the Map object and to restrict the type for the parameters. This is the type of the Product.name property for the object created in the listing, which is string. |
propertyName | This is the key property name, which is required as a value that can be used by the JavaScript runtime after the TypeScript generic type information has been removed. For the object created in the listing, this value is name, corresponding to the generic type K. |

A mapped type
The property name selector defines a type parameter, named P in this example, and uses the in keyword to enumerate the types in a literal value union. The type union can be expressed directly, such as "name"|"price", or obtained using keyof.
The TypeScript compiler creates a new property in the mapped type for each of the types in the union. The type of each property is determined by the type selector, which can be obtained from the source type using the indexed access operator with P as the literal value type to look up.
Type mapping produces shapes that can be used for object literals, implemented by classes, or extended by interfaces. Type mapping does not produce a class, however.
A question mark (the ? character) is placed after the name selector to make the properties in the mapped type optional, and a minus sign and a question mark (the -? characters) are used to make properties required. Properties are made read-only and read-write by preceding the name selector with readonly and -readonly.
The types produced by mappings can be fed into other mappings, creating a chain of transformations. In the listing, the type produced by the MakeOptional<T> mapping is then transformed by the MakeRequired<T> mapping, the output of which is then fed to the MakeReadOnly<T> mapping and then the MakeReadWrite<T> mapping. The result is that properties are made optional and then required and then read-only and, finally, read-write.
Name | Description |
|---|---|
Partial<T> | This mapping makes properties optional. |
Required<T> | This mapping makes properties required. |
Readonly<T> | This mapping adds the readonly keyword to properties. |
Pick<T, K> | This mapping selects specific properties to create a new type, as described in the “Mapping Specific Properties” section. |
Omit<T, keys> | This mapping selects specific properties to create a new type, as described in the “Mapping Specific Properties” section. |
Record<T, K> | This mapping creates a type without transforming an existing one, as explained in the “Creating Types with a Type Mapping” section. |

A conditional type
A conditional type is a placeholder for one of its result types, which isn’t chosen until the generic type parameter is used, which allows the expression to be evaluated using one of the result types selected.
Conditional types are an advanced feature that should be used carefully. Writing conditional types can be a tortured process and can often feel like sleight of hand as you lead the compiler through a series of expressions to get the results you require.
As the complexity of a conditional type increases, so does the danger that you won’t capture all of the permutations of types correctly and create a result that is too lax, creating a type checking hole, or too restrictive, causing compiler errors for valid uses.
When using conditional types, remember that you are only describing combinations of types to the TypeScript compiler and that the type information will be removed during compilation. And, as a conditional type becomes more complex and encompasses more combinations, you should take a moment to consider if there is a simpler way to achieve the same result.
The type nestedType<T> is a nested conditional type to select between three result types, based on the value of the generic type parameter. As noted in the sidebar, complex conditional types can be difficult to understand, and this is especially true when they are nested.
At the time of writing, the TypeScript compiler has difficulty correlating the data type of values returned by methods and functions when conditional types are used. It is for this reason that Listing 13-25 uses a type assertion in the total method to tell the compiler to treat the result as any. Without the type annotation, the compiler will report an error.
Name | Description |
|---|---|
Exclude<T, U> | This type excludes the types that can be assigned to U from T, equivalent to the Filter<T, U> type in Listing 13-26. |
Extract<T, U> | This type selects the types that can be assigned to U from T. |
NonNullable<T> | This type excludes null and undefined from T. |

Inferring a type in a conditional type
Name | Description |
|---|---|
Parameters<T> | This conditional type selects the types of each function parameter, expressed as a tuple. |
ReturnType<T> | This conditional type selects the function result type, equivalent to Result<T> in Listing 13-31. |
ConstructorParameters<T> | The conditional type selects the types of each parameter of a constructor function, expressed as a tuple, as demonstrated after the table. |
InstanceType<T> | This conditional type returns the result type of a constructor function. |
In this chapter, I described the advanced generic type features that TypeScript provides. These are not required in every project, but they are invaluable when the more basic features cannot describe the types that an application requires. In the next chapter, I explain how TypeScript deals with JavaScript code, both when it is directly part of the project and also when it is in third-party packages on which the application depends.
Problem | Solution | Listing |
|---|---|---|
Incorporate JavaScript files in a project | Enable the allowJs and checkJs compiler options | |
Control whether a JavaScript file is checked by the TypeScript compiler | Use the @ts-check and @ts-nocheck comments | |
Describe JavaScript types | Use JSDoc comments or create a declaration file | |
Describe third-party JavaScript code | Update the compiler configuration and create a declaration file | |
Describe third-party code without creating a declaration file | Use a package that contains a declaration file or install a publicly available type declaration package | |
Generate declaration files for use in other projects | Enable the declaration compiler option |
Name | Description |
|---|---|
allowJs | This option includes JavaScript files in the compilation process. |
baseUrl | This option specifies the root location used to resolve module dependencies. |
checkJs | This option tells the compiler to check JavaScript code for common errors. |
declaration | This option produces type declaration files when enabled, which describe the types for use in other projects. |
esModuleInterop | This option adds helper code for importing from modules that do not declare a default export and is used in conjunction with the allowSyntheticDefaultImports option. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
paths | This option specifies the locations used to resolve module dependencies. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
To prepare the project for this chapter, open a new command prompt, navigate to a convenient location, and create a folder named usingjs. Run the commands shown in Listing 14-1 to navigate into the new folder and tell the Node Package Manager (NPM) to create a package.json file, which will track the packages added to the project.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
These configuration settings tell the TypeScript compiler to generate code for the most recent JavaScript implementations, using the src folder to look for TypeScript files and using the dist folder for its outputs. The module setting tells the compiler that the CommonJS modules are required, which is the format supported by Node.js.
The code in the index.ts file creates some SportsProduct objects, uses them to populate a Cart, and writes details of the Cart contents to the console.
The examples in this book have all assumed that you are working purely in TypeScript. Often, this won’t be possible, either because TypeScript is introduced partway through a project or because you need to work with JavaScript code that has already been developed in earlier projects.
A project can contain TypeScript and JavaScript code side by side, requiring only changes to the TypeScript compiler and some optional steps to describe the types used by the JavaScript code. To demonstrate the process, some JavaScript code is required. Add a file called formatters.js to the src folder with the code shown in Listing 14-9.
The file extension for the file in Listing 14-9 is js because this is a pure JavaScript file. It is important to use the right extension for the examples in this section.
The TypeScript compiler locates the JavaScript code without difficulty but doesn’t copy the code into the dist folder, which means that the Node.js runtime can’t locate the JavaScript code at runtime.
Configuring the TypeScript compiler to include JavaScript files allows code to be easily mixed and ensures that JavaScript features are versioned consistently.
Name | Description |
|---|---|
//@ts-check | This comment tells the compiler to check the contents of a JavaScript file even when the checkJs property in the tsconfig.json file is false. |
//@ts-nocheck | This comment tells the compiler to ignore the contents of a JavaScript file, even when the checkJs property in the tsconfig.json file is true. |
This issue can be resolved by providing the compiler with type information that describes the JavaScript code so that its use can be checked during compilation. There are two approaches to describing types in JavaScript code, which I demonstrate in the following sections.
The TypeScript compiler can obtain type information when it is included in JSDoc comments. JSDoc is a popular markup language used to annotate JavaScript code as comments. Listing 14-16 adds JSDoc comments to the formatters.js file.
Many code editors will help generate JSDoc comments. Visual Studio Code, for example, responds when a comment is created and automatically generates a list of function parameters.
The JSDoc specification allows types to be indicated for function parameters. The JSDoc comment in Listing 14-16 indicates that the costFormatter function expects to receive string and number parameters. The type information is a standard part of JSDoc, but it is usually just to provide guidance.
The compiler has read the JSDoc comment for the costFormatter function and determined that the value used to invoke the function in the index.ts file doesn’t use the right data type.
See https://github.com/Microsoft/TypeScript/wiki/JSDoc-support-in-JavaScript for a complete list of the JSDoc tags that the TypeScript compiler understands.
The contents of a type declaration file mirror those of the code file it describes. Each statement contains the declare keyword, which tells the compiler that the statement describes the types defined elsewhere. Listing 14-18 describes the parameters and result types of the functions that are exported from the formatters.js file.
Type declaration files take precedence over JSDoc comments when both are used to describe JavaScript code.
When a type declaration file is used, it must describe all the features defined in the corresponding JavaScript file that is used by the application because it is the only source of information used by the TypeScript compiler, which no longer examines the JavaScript file. For the example project, this means that the type declaration in Listing 14-18 must describe the sizeFormatter and costFormatter functions since both are used in the index.ts file. Any feature that is not described in the type declaration file will not be visible to the TypeScript compiler. To demonstrate, Listing 14-19 changes the writeMessage function in the formatters.js file so that is exported for use in the rest of the application.
The TypeScript compiler trusts that the contents of a type declaration file are accurate, which means you are responsible for ensuring the types you select are supported by the JavaScript code and that all of the features in the JavaScript code are implemented as you describe.
The debug package is a utility package that provides decorated debugging output to the JavaScript console. I have chosen it for this chapter because it is small but well-written and widely used in JavaScript development.
The compiler will try to infer types for third-party packages but will have the same limited success as for JavaScript files in the project. A type declaration file can be created for packages installed in the node_modules folder, although the technique is awkward; a better approach is to use publicly available definitions, as described in the next section.
The paths property is used to specify locations that the TypeScript compiler will use as it tries to resolve import statements for modules. The configuration used in the listing tells the compiler to look for all packages in a folder called types. When the paths property is used, the baseUrl property must also be specified, and the value used in the listing tells the compiler that the location specified by the path property can be found in the same folder as the tsconfig.json file.
The process for describing a third-party module can be complicated, not least because the package authors may not have anticipated that someone would try to describe their code using static types. To further complicate matters, the wide range of JavaScript language versions and module formats means that arcane incantations can be required to present TypeScript with descriptions that are useful and accurately represent the code in the module.
The two interfaces in Listing 14-24 describe the most basic features of the debug package, allowing a simple debugger to be set up and used. The last two statements are required to represent the exports from the package to TypeScript.
See https://github.com/visionmedia/debug for details of the full API provided by the debug package.
This error would not have been reported without the declaration file because pure JavaScript doesn’t require that the number of arguments used to invoke a function matches the number of parameters it defines, as explained in Chapter 8.
You may see different locations reported on your development machine, but the message will confirm that the compiler has located the custom declaration file and will use it to resolve dependencies on the debug package.
The declaration file in Listing 14-24 shows that it is possible to describe publicly available packages, but it is not a process that I recommend, to the extent that I don’t provide any detail about the different ways that package contents can be described.
First, it can be difficult to accurately represent someone else’s code, and creating an accurate type declaration file can require a detailed analysis of a package and a solid understanding of what it does and how it works. Second, custom declarations tend to focus on just the features that are immediately required, and declaration files get patched up and extended as further features are needed, producing results that are difficult to understand and manage. Third, each new release means that the declaration file must be revisited to ensure that it still accurately reflects the API presented by the package.
But, the most compelling reason not to create your own declaration files is that there is an excellent library of high-quality declarations for thousands of JavaScript packages available through the Definitely Typed project, as described in the next section. And the increased popularity of TypeScript means that more packages come with type declaration files built in.
If you are determined to write your own files—or you want to contribute to the Definitely Typed project—then Microsoft has produced a dedicated guide to describing packages, which can be found at https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html.
The name used for the Definitely Typed package is @types/ followed by the name of the package for which a description is required. For the debug package, for example, the Definitely Typed package is called @types/debug.
Notice that a version number for the @types/debug package is not specified in Listing 14-27. When installing @types packages, I let NPM select the package version.
The compiler won’t use the Definitely Typed declarations until the configuration is changed to stop the compiler from looking in the types folder, as shown in Listing 14-28.
The configuration change is required because the project contains custom and Definitely Typed declarations for the same package. This won’t be a problem in real projects, and you can use the configuration settings to choose between custom and Definitely Typed declarations for each package you use.
The compiler looks in the node_modules/@types folder, which contains folders that correspond to each of the packages for which there are declaration files, following the same pattern as for custom files. (No configuration changes are required to tell the compiler to look in the node_modules@types folder.)

Using the Chalk package
If your code is going to be used by other projects, you can ask the compiler to generate declaration files alongside the pure JavaScript, which has the effect of preserving the type information for other TypeScript programmers but still allowing the project to be used as regular JavaScript.
This is the JavaScript code from the formatters.js file but with type annotations. Listing 14-37 updates the index.ts file to depend on the TypeScript file instead of the JavaScript file.
It is important to follow through with the changes in this process because disabling the allowJS option only prevents the compiler from adding the JavaScript file to the output folder. It doesn’t prevent any of the TypeScript code from depending on the JavaScript file, which can lead to runtime errors because the JavaScript runtime won’t be able to find all the files it needs.

Generating declaration files
In this chapter, I showed you how to work with JavaScript in a TypeScript project. I explained how to configure the compiler to process and type check JavaScript files and how declaration files can be used to describe JavaScript code to the compiler. In the next part of the book, I build a series of web applications that rely on TypeScript, starting with a stand-alone application and then using the Angular, React, and Vue.js frameworks.
In this part of the book, I show you how TypeScript fits into the development process for the three most popular web application frameworks: Angular, React, and Vue.js. In each case, I go through the process of creating the project, setting up a web service, and writing a simple web application. In this chapter, I create the same web application without using any of these frameworks, providing a baseline for understanding the features they provide and context for how TypeScript features are used.
Name | Description |
|---|---|
jsx | This option specifies how HTML elements in TSX files are processed. |
jsxFactory | This option specifies the name of the factory function that is used to replace HTML elements in TSX files. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
To prepare for this chapter, open a new command prompt, navigate to a convenient location, and create a folder called webapp. Run the commands shown in Listing 15-1 to move to the webapp folder and to tell the Node Package Manager (NPM) to create a file named package.json.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.

The initial project toolchain
The development tools are hidden when you use a framework like Angular, React, or Vue.js, as demonstrated in later chapters, but for this chapter, I am going to install and configure each tool and show you how they work together.
When the application is executed using Node.js in the project folder, any import statements can be resolved using the JavaScript generated by the TypeScript compiler or by the packages installed in the node_modules folder.
The JavaScript runtime starts with the application entry point—the index.js file that is compiled from the index.ts file—and processes the import statements it contains. For each import statement, the runtime resolves the dependency and loads the required module, which will be another JavaScript file. Any import statements declared in the new JavaScript file are processed in the same way, allowing all the dependencies in the application to be resolved so the code can be executed.
The JavaScript runtime doesn’t know in advance what import statements each code file may contain and so it doesn’t know which JavaScript files are required. But it doesn’t matter because looking for files to resolve dependencies is a relatively quick operation since all the local files are easily accessible.
This approach doesn’t work as well for web applications, which don’t have direct access to the file system. Instead, files have to be requested over HTTP, which can be a slow and expensive operation and which doesn’t lend itself to easily checking multiple locations to resolve dependencies on files. Instead, a bundler is used, which resolves the dependencies during compilation and packages all the files that the application uses into a single file. One HTTP request delivers all the JavaScript required to run the application, and other content types, such as CSS, can be included in the file produced by the bundler, which is known as a bundle. During the bundling process, the code and content can be minified and compressed, reducing the amount of bandwidth required to deliver the application to the client. Large applications can be split into multiple bundles so that optional code or content can be loaded separately and only when it is required.
This entry and output settings tell webpack to start with the src/index.ts file when resolving the application’s dependencies and to give the bundle file the name bundle.js. The other settings configure webpack to use the ts-loader package to process files with the ts file extension.
See https://webpack.js.org for details of the full range of configuration options that webpack supports.

Adding a bundle to the toolchain
When the browser receives the HTML file, it will process the contents and encounter the script element, which will trigger an HTTP request for the bundle.js file, which contains the application’s JavaScript code.

Displaying the HTML file
When WDS is started, webpack is put into a watch mode that builds a new bundle when a change to the code files is detected. During the bundling process, WDS injects additional code into the JavaScript file that opens a connection back to the server and waits for a signal to reload the browser, which is sent for each new bundle. The effect is that the browser is reloaded automatically each time a change is detected and processed, which can be seen by adding a statement to the index.ts file, as shown in Listing 15-14.
The reload feature works only for code files and doesn’t apply to the HTML file in the assets folder. Changes to the HTML file take effect only when WDS is restarted.

Adding WDS to the development toolchain
This toolchain contains the key elements that you will see in most web application projects, although the individual parts are often hidden from sight. Notice how the TypeScript compiler is just one part of the chain, allowing TypeScript code to be integrated into a set of broader JavaScript development tools.
The Product, Order, and OrderLine types are all exported so they can be used outside of the code file. The Order class represents the user’s product selections, each of which is expressed as an OrderLine object that combines a Product and a quantity. I have defined Product as a type alias because this will simplify working with data obtained remotely when I introduce a web service in Chapter 15. The Order and OrderLine types are defined as classes because they define additional features beyond being a collection of related properties.
The DomDisplay class defines a getContent method whose result is an HTMLElement object, which is the type used by the DOM API to represent an HTML element. The getContent method creates an H3 element and uses a template string to set its content. The element is added to four classes, which will be used to manage the appearance of the element when it is displayed. The data values used in the template string are provided through a property named props. This is a convention that was adopted from the React framework, which I explain in the “Using JSX to Create HTML Content” section and demonstrate in Chapter 19.
The three classes to which the h3 element is assigned in Listing 15-19 correspond to styles defined by Bootstrap, which is a high-quality, open-source CSS framework that makes it easy to consistently style HTML content.
The webpack configuration can be extended with loaders for additional content types that are included in the bundle file, which means that the development toolchain can be extended to include support for CSS stylesheets, such as the one that defines the Bootstrap styles applied to the h3 element.
Stop the WDS process using Control+C and run the commands shown in Listing 15-20 in the webapp folder to install the CSS loaders and Bootstrap packages.
I use the Bootstrap CSS framework in most of my projects because it is easy to work with and produces good results. See https://getbootstrap.com for details of the styles available and of the optional JavaScript features that are available.
The DOM API provides a complete set of features to work with the HTML document displayed by the browser, but the result can be verbose code that is difficult to read, especially when the content to be displayed depends on the result of background tasks, such as getting data from a web service.
The code in Listing 15-22 has to wait for two tasks to be completed before it can display any content. The browser has to complete processing the HTML document contained in the index.html file before the DOM API can be used to manipulate its contents. Browsers process HTML elements in the order in which they are defined in the HTML document, which means that the JavaScript code will be executed before the browser has processed the elements in the body section of the document. Any attempt to modify the document before it has been fully processed can lead to inconsistent results.
The default settings for the TypeScript compiler include type declaration files for the DOM API, which allows type-safe use of the browser features.
The code in Listing 15-22 also has to wait for the data source to obtain its data. The LocalDataSource class uses local test data that is immediately available, but there may be a delay when the data is retrieved from a web service, which I implement in Chapter 16.
When both tasks are complete, the placeholder element in the index.html file is removed and replaced with the HTMLElement object obtained by creating a DomDisplay object and calling its getContent method.

Generating HTML elements
The loaders added to the project deal with CSS by adding JavaScript code that is executed when the contents of the bundle file are processed. This code uses an API provided by the browser to create the CSS styles. This approach means that the bundle file contains only JavaScript even though it delivers different types of content to the client.
Expressing HTML elements using JavaScript statements is awkward, and using the DOM API directly produces verbose code that is difficult to understand and prone to errors, even with the static type support that TypeScript provides.
The problem isn’t the DOM API itself—although it hasn’t always been designed with ease of use in mind—but the difficulty in using code statements to create declarative content like HTML elements. A more elegant approach is to use JSX, which stands for JavaScript XML and which allows declarative content such as HTML elements to be easily mixed with code statements. JSX is most closely associated with React development—as demonstrated in Chapter 19—but the TypeScript compiler provides features that allow it to be used in any project.
JSX isn’t the only way to simplify working with HTML elements, but I have used it in this chapter because the TypeScript compiler supports it. If you don’t like JSX, you can use one of the many JavaScript template packages available (search for mustache templates to get started).
This file uses JSX to create the same result as the regular TypeScript class. The difference is the getContent method, which returns an HTML element expressed directly as an element, instead of using the DOM API to create an object and configure it through its properties. The h3 element returned by the h3 element is expressed in a way that is similar to an element in an HTML document, with the addition of fragments of JavaScript that allow expressions to generate content dynamically based on the values provided through the props property.
This file won’t compile because the project has not yet been configured for JSX, but you can see how this format can be used to create content more naturally. In the sections that follow, I will explain how JSX files are processed and configure the example project to support them.
When a TypeScript JSX file is compiled, the compiler processes the HTML elements it contains to transform them into JavaScript statements. Each element is parsed and separated into the tag that defines the element type, the attributes applied to the element, and the element’s content.

Transforming JSX
When the application runs, each call to the factory function is responsible for using the tag name, attribute, and content parsed by the compiler to create the HTML element the application requires.
The elements in a JSX file are not standard HTML. The key difference is that the attributes on the elements use the JavaScript property names defined by the DOM API instead of the corresponding attribute names from the HTML specification. Many of the properties and attributes share the same name, but there are some important differences, and the one that causes the most confusion is the class attribute, which is used to assign elements to one or more classes, typically so they can be styled.
This is the reason that TypeScript JSX classes receive their data values through the property named props, because each prop corresponds to a property that must be set on the HTMLElement object created by the factory function. Forgetting to use property names in a JSX file is a common mistake and is a good place to start checking when you don’t get the results you expect.
Name | Description |
|---|---|
jsx | This option determines the way that the compiler handles elements in a TSX file. The react setting replaces HTML elements with calls to the factory function and emits a JavaScript file. The react-native setting emits a JavaScript file that leaves the HTML elements intact. The preserve setting emits a JSX file that leaves the HTML elements intact. The react-jsx and react-jsx settings use __jsx as the name of the function that creates elements. |
jsxFactory | This option specifies the name of the factory function, which the compiler will use when the jsx option is set to react. |
The change to the resolve setting tells webpack that TSX files should be included in the bundle, and the other change specifies that TSX files will be handled by the ts-loader package, which will use the TypeScript compiler.
The createElement function in Listing 15-27 does the bare minimum to create HTML elements using the DOM API without any of the sophisticated features provided by the frameworks used in later chapters. The tag parameter can be a function, in which case another class that uses JSX has been specified as the element type.
The last section of code in Listing 15-27 is a specific incantation that tells the TypeScript compiler that it should use the props property to perform type checking on the values assigned to JSX element attributes in TSX files. This relies on the TypeScript namespace feature, which I have not described in this chapter because it has been superseded by the introduction of standard JavaScript modules and is no longer recommended for use.
The JSX class is a drop-in replacement for the class that uses the DOM API directly. In later sections, you will see how classes that use JSX can be combined using only elements, but there is always a boundary between a regular class and one that contains HTML elements. For the example application, that boundary will be between the index file and HtmlDisplay class.

Rendering content using JSX
Now that the basic structure of the application is in place, I can add features, starting with a display of products that can be filtered by category.
The ProductItem class receives a Product object and a callback function through its props. The getContent method renders HTML elements that display the details of the Product object, along with a select element that allows a quantity to be selected and a button that the user will click to add items to the order.
The fat arrow syntax ensures that the this keyword refers to the ProductItem object, which allows the props and quantity properties to be used. If a conventional method is used to handle an event, this refers to the object that describes the event.
To read the value property from the select element, I have to apply an assertion to tell the TypeScript compiler that the event.target property will return an HTMLSelectElement object.
The HTMLSelectElement type is one of the standard DOM API types, which are described in detail at https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement.
This pattern is common when JSX is used so that classes render HTML elements using data received via props; this props also includes callback functions that are invoked in response to events. In this case, the onclick attribute is used to invoke the function received through the callback prop.
When it parses the TSX file, the TypeScript compiler detects that the custom tag creates a statement that invokes the factory function with the corresponding class. At runtime, a new instance of the class is created, the attributes of the element are assigned to the props property, and the getContent method is called to get the content to include in the HTML presented to the user.
I need to create a bridge between the features of the data store and the JSX classes that display content to the user, ensuring that the content is updated to reflect changes in the application state. The frameworks demonstrated in later chapters take care of handling updates efficiently and minimizing the amount of work the browser does to display changes.
The methods defined by the HtmlDisplay class are used as the callback functions for the ProductList class, which passes them on to the ProductItem and CategoryList classes. When these methods are invoked, they update the properties that keep track of the application state and then call the updateContent method, which replaces the HTML rendered by the class.

Displaying products
In this chapter, I showed you how to create a simple but effective development toolchain for web application development using the TypeScript compiler and webpack. I showed you how the output from the TypeScript compiler can be incorporated into a webpack bundle and how the support for JSX can be used to simplify working with HTML elements. In the next chapter, I complete the stand-alone web application and prepare it for deployment.
Name | Description |
|---|---|
emitDecoratorMetadata | This option includes decorator metadata in the JavaScript emitted by the compiler. |
experimentalDecorators | This option enables support for decorators. |
jsx | This option specifies how HTML elements in TSX files are processed. |
jsxFactory | This option specifies the name of the factory function that is used to replace HTML elements in TSX files. |
moduleResolution | This option specifies the style of module resolution that should be used to resolve dependencies. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
rootDir | This option specifies the root directory that the compiler will use to locate TypeScript files. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
In this chapter, I continue to use the webapp project created in Chapter 15. To prepare for this chapter, open a new command prompt, navigate to the webapp folder, and run the commands shown in Listing 16-1 to add new packages to the project.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
The json-server package is a RESTful web service that will provide data for the application, replacing the local test data used in Chapter 15. The npm-run-all package is a useful tool for running multiple NPM packages from a single command.
The json-server package will be configured to use the data in Listing 16-2, which will cause it to reset each time it is restarted. (The package can also store data persistently, but that is not as useful for example projects where a known baseline is more useful.)

Getting data from the web service

Running the example application
Many packages are available for making HTTP requests in JavaScript applications, all of which use APIs provided by the browser. In this chapter, I am using the Axios package, which is a popular choice because it is easy to work with and comes complete with TypeScript declarations. To create a data source that uses HTTP requests, add a file called remoteDataSource.ts in the src/data folder and add the code shown in Listing 16-6.
There are two APIs provided by browsers for making HTTP requests. The traditional API is XmlHttpRequest and is supported by all browsers, but it difficult to work with. There is a new API, named Fetch, that is easier to work with but is not supported by older browsers. You can use either API directly, but packages like Axios provide an API that is easy to work with while preserving support for older browsers.
The Axios package provides get and post methods that send HTTP requests with the corresponding verbs. The implementation of the loadProducts method sends a GET request to the web service to get the product data. The storeOrder method transforms the details of the order to a shape that can be easily stored and sends the data to the web service as a POST request. The web service will respond with the object that has been stored, which includes an id value that uniquely identifies the stored object.

Using remote data
Writing decorators can be difficult because they rely on a set of nested functions. The minimumValue function receives parameters that contain the name of the property to operate on and the minimum value to apply. The result is a function that is invoked at runtime and whose parameters are the class to which the decorator has been applied, the name of the method, and a PropertyDescriptor object that describes the method. The PropertyDescriptor type is an interface provided by TypeScript that describes the shape of JavaScript properties. For methods, the PropertyDescriptor.value property is used to store the function, and this is replaced by an implementation that invokes the original method and then processes the result to set the minimum property value.

Using a decorator to enforce a minimum value
Name | Description |
|---|---|
design:type | This item describes what the decorator has been applied to. For the decorator in Listing 16-12, this will be Function. |
design:paramtypes | This item describes the types of the parameters of the function to which the decorator has been applied. For the decorator in Listing 16-12, this will be [String, String], indicating two parameters, both of which accept string values. |
design:returntype | This item describes the result type of the function to which the decorator has been applied. For the decorator in Listing 16-12, this will be Promise. |
This example mixes features that are closely associated with the React and Angular frameworks, showing that both are built on standard features and both can be used in the same application (even though this is rarely done in real projects).

Using a decorator to modify HTML elements
Much of Chapter 15 was spent setting up the development tools and configuring the project to deal with JSX, which makes it easier to work with HTML content in code files. Now that the basic structure of the application is in place, adding new features is relatively simple. There are no new TypeScript features in this section of the chapter, which just completes the application.
This class receives an Order object and a callback function through its props. A simple summary of the Order is displayed, along with a button that invokes the callback function when it is clicked.
The OrderDetails class displays a table containing the details of the order, along with buttons to return to the product list or to submit the order.
This class displays a simple message that contains the unique ID assigned by the web service and a button that invokes a callback received as a prop when it is clicked.
The additions to the HtmlDisplay class are used to determine which JSX classes are used to display content to the user. The key is the mode property, which uses the values of the DisplayMode enum to select content, combined with the showDetails, showList, and submitOrder methods, which change the mode value and update the display.
There can often be a single class in a web application that becomes a point where complexity is concentrated, even in a simple application like this one. Using one of the frameworks described in the chapters that follow can help but simply expresses it in a different way, most often in a complex set of mappings between the URLs the application supports and the content classes that they correspond to.

Using the example application
When you submit an order, you can see the data that the server has stored by navigating to http://localhost:4600/orders, as shown in Figure 16-7.
The orders are not stored persistently and will be lost when the web service is stopped or restarted. Persistent storage is added in the next section.

Inspecting the submitted orders
The Webpack Development Server and the toolchain that provides it with the bundle cannot be used in production, so some additional work is required to prepare an application for deployment, as described in the following sections.
The Webpack Development Server should not be used in production because the features it provides are focused on creating bundles dynamically based on changes in the source code. For production, a regular HTTP server is required to deliver the HTML, CSS, and JavaScript files to the browser, and a good choice for simple projects is the open-source Express server, which is a JavaScript package that is executed by the Node.js runtime. Use Control+C to stop the development tools, and use the command prompt to run the command shown in Listing 16-23 in the webapp folder to install the express package.
The express package may already be installed because it is used by other tools. Even so, it is good practice to add the package because it adds a dependency in the project.json file.
This is the same product information I added to the JavaScript file in Listing 16-2, but it is expressed in JSON format, which means that the stored order data won’t be lost when the application is stopped or restarted.
The statements in the server.js file configure the express and json-server packages so that the contents of the dist and assets folders are used to deliver static files and so URLs prefixed with /api will be handled by the web service.
You can write server code like this in TypeScript and then compile it to generate the JavaScript that will be executed in production. This is a good idea if you have especially complex server code, but I find working directly in JavaScript easier for simple projects that are only combining the features provided by different packages.
The URLs are specified relative to the one used to request the HTML document, following the common convention that data requests are prefixed with /api.
The TypeScript files are compiled into JavaScript, just as they were in development, and the bundle file is written to the dist folder. The warnings about the size of the files that have been created can be ignored.

Running the production build
To complete this chapter, I am going to create a container for the example application so that it can be deployed into production. At the time of writing, Docker is the most popular way to create a container, which is a pared-down version of Linux with just enough functionality to run the application. Most cloud platforms or hosting engines have support for Docker, and its tools run on the most popular operating systems.
The first step is to download and install the Docker tools on your development machine, which is available from www.docker.com/products/docker. There are versions for macOS, Windows, and Linux, and there are some specialized versions to work with the Amazon and Microsoft cloud platforms. The free Community edition is sufficient for this chapter.
One drawback of using Docker is that the company that produces the software has gained a reputation for making breaking changes. This may mean that the example that follows may not work as intended with later versions. If you have problems, check the repository for this book for updates (https://github.com/Apress/essential-typescript-4) or contact me at adam@adam-freeman.com.
The devDependencies section specifies the packages required to run the application in the container. All of the packages for which there are import statements in the application’s code files will have been incorporated into the bundle created by webpack and are listed. The other fields describe the application, and their main use is to prevent a warning when the container is created.
The contents of the Dockerfile use a base image that has been configured with Node.js and copies the files required to run the application, including the bundle file containing the application and the file that will be used to install the NPM packages required to run the application in deployment.
An image is a template for containers. As Docker processes the instructions in the Docker file, the NPM packages will be downloaded and installed, and the configuration and code files will be copied into the image.

Running the containerized application
The application is ready to deploy to any platform that supports Docker.
In this chapter, I completed the development of the stand-alone web application by adding a data source that consumed a web service and by adding JSX classes that displayed different content to the user. I finished by preparing the application for deployment and creating a Docker container image. In the next chapter, I build a web application using the Angular framework.
Name | Description |
|---|---|
baseUrl | This option specifies the root location used to resolve module dependencies. |
declaration | This option produces type declaration files when enabled, which describe the types for use in other projects. |
downlevelIteration | This option enables support for iterators when targeting older versions of JavaScript. |
experimentalDecorators | This option determines whether decorators are enabled. |
importHelpers | This option determines whether helper code is added to the JavaScript to reduce the amount of code that is produced overall. |
lib | This option selects the type declaration files the compiler uses. |
module | This option determines the style of module that is used. |
moduleResolution | This option specifies how modules are resolved. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
sourceMap | This option determines whether the compiler generates source maps for debugging. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
Angular projects are most easily created using the angular-cli package. Open a command prompt and run the command shown in Listing 17-1 to install the angular-cli package.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
Question | Answer |
|---|---|
Do you want to enforce stricter type checking and stricter bundle budgets in the workspace? | No |
Would you like to add Angular routing? | Yes |
Which stylesheet format would you like to use? | CSS |
It can take a few minutes for the project to be created because a large number of JavaScript packages must be downloaded.
These entries allow both the web service that will provide the data and the Angular development tools to be started with a single command.
The Angular development tools require a configuration change to incorporate the Bootstrap CSS stylesheet in the application. Open the angular.json file in the angularapp folder and add the item shown in Listing 17-7 to the build/styles section.
There are two styles settings in the angular.json file, and you must take care to change the one in the build section and not the test section. If you don’t see styled content when you run the example application, the likely cause is that you have edited the wrong section.
This is the Component decorator, which describes a class that will generate HTML content, similar in purpose to the JSX classes I created in the stand-alone web app in Chapters 15 and 16.
The configuration writes the compiled JavaScript files to the dist/out-tsc folder, although you won’t see that folder in the project because webpack is used to create a bundle automatically.
The most important setting is experimentalDecorators, which enables decorators, as described in Chapter 16. This feature—more than any other feature provided by TypeScript—is essential for Angular development.
Care is required when making changes to the tsconfig.json file because they can break the rest of the Angular toolchain. Most changes in an Angular project are applied through the angular.json File.
This is the same code used in Chapter 15 and requires no changes because Angular uses regular TypeScript classes for its data model entities.
When a new DataSource object is needed, Angular will inspect the constructor, create a DataSourceImpl object, and use it to invoke the constructor to create the new object, a process known as injection. The Injectable decorator tells Angular that other classes can declare dependencies on the DataSource class. The DataSourceImpl class is abstract, and the DataSource class has no idea which concrete implementation class will be used to resolve its constructor dependency. The selection of the implementation class is made in the application’s configuration, as shown in Listing 17-12.
In this situation, I am using the Observable class as a direct replacement for the standard JavaScript Promise. The Observable class provides sophisticated features for dealing with complex sequences, but the advantage here is that Angular will update the content presented to the user when the Observable produces a result, which means that the rest of the DataSource class can be written without needing to deal with asynchronous tasks.
The type argument is used for the result from the get method, which is an Observable that will generate a sequence of the specified type, which is Product[] in this case.
The generic type arguments for the HttpClient methods are standard TypeScript. There is no Angular magic happening behind the scenes, and the developer remains responsible for specifying a type that will correspond to the data received from the server.
The pipe method is used with the map function to create an Observable that generates values based on those from another Observable. This allows me to receive the result from the HTTP POST request and extract just the id property from the result.
In the stand-alone web application, I created an abstract data source class and created subclasses that provided local or web service data, which was loaded by a method called in the abstract class constructor. This is an approach that doesn’t work well in Angular because the HttpClient is not assigned to an instance property until after the abstract class constructor is invoked with the super keyword, which means the subclass is asked to get data before it has been properly set up. To avoid this problem, I separated just the part of the data source that deals with the data into the abstract class.
The DataModelModule class is defined just so that the NgModule decorator can be applied. The decorator’s imports property defines the dependencies that the data model classes require, and the providers property defines the classes in the Angular module that can be injected into the constructors of other classes in the application. For this module, the imports property tells Angular that the module that contains the HttpClient class is required, and the providers property tells Angular that the DataSource class can be used for dependency injection and that dependencies on the DataSourceImpl class should be resolved using the RemoteDataSource class.
Angular splits the generation of HTML content into two files: a TypeScript class to which the Component decorator is applied and an HTML template that is annotated with directives that direct the generation of dynamic content. When the application is executed, the HTML template is compiled, and the directives are executed using the methods and properties provided by the TypeScript class.
The Component decorator configures the component. The selector property specifies the CSS selector that Angular will use to apply the component to the application’s HTML, and the templateUrl property specifies the component’s HTML template. For the ProductItem class, the selector property tells Angular to apply this component when it encounters the product-item element and that the component’s HTML template can be found in a file called productItem.component.html in the same directory as the TypeScript file.
Angular uses the Input decorator to denote the properties that allow components to receive data values through HTML element attributes. The Output decorator is used to denote the flow of data out from the component through a custom event. The ProductItem class receives a Product object, whose details it displays to the user, and triggers a custom event when the user clicks a button, accessible through the addToCart property.
Expressions are evaluated in the context of the component, so this fragment reads the value of the product.price property, invokes the toFixed method, and inserts the result into the enclosing span element.
The ngModel directly is applied with square brackets and parentheses and creates a two-way binding between the select element and the component’s quantity property. Changes to the quantity property will be reflected by the select element, and values picked using the select element are used to update the quantity property.
This template uses the ngFor directive to generate a button element for each of the values returned by the categories property. The asterisk (the * character) that prefixes ngFor indicates a concise syntax that allows the ngFor directive to be applied directly to the element that will be generated.
The square brackets allow the value of the class attribute to be set using a JavaScript expression, which is the result of calling the component’s getBtnClass method.
The ProductList class declares a dependency on the DataSource class and defines products and categories methods that return data from the DataSource. There are three methods that respond to user interaction: handleCategorySelect will be invoked when the user clicks a category button, handleAdd will be invoked when the user adds a product to the order, and handleSubmit will be called when the user wants to move on to the order summary. The handleSubmit method writes out a message to the console and will be fully implemented in Chapter 18.
The header tag corresponds to the selector setting for the Component decorator applied to the Header class in Listing 17-17. The order attribute is used to provide a value for the Input property of the same name defined by the Header class and allows ProductList to provide Header with the data it requires. The submit attribute corresponds to the Output property defined by the Header class and allows ProductList to receive notifications. The ProductList template uses header, category-list, and product-item elements to display the Header, CategoryList, and ProductItem components.
The NgModule decorator’s declarations property is used to declare the components that the application requires and is used to add the classes defined in the previous sections. The imports property is used to list the other modules the application requires and has been updated to include the data model module defined in Listing 17-12.

Displaying content to the user
The user can filter the list of products and add products to the order. Clicking Submit Order only writes a message to the browser’s JavaScript console, but I’ll add support for the rest of the application’s workflow in the next chapter.
In this chapter, I explained the role that TypeScript has in Angular development. I explained that TypeScript decorators are used to describe the different building blocks that can be used in an Angular application. I also explained that Angular HTML templates are compiled when the browser executes the application, which means that TypeScript features have already been removed and cannot be used in templates. In the next chapter, I complete the application and prepare it for deployment.
Name | Description |
|---|---|
baseUrl | This option specifies the root location used to resolve module dependencies. |
declaration | This option produces type declaration files when enabled, which describe the types for use in other projects. |
downlevelIteration | This option includes helper code to support iterators on older JavaScript runtimes. |
emitDecoratorMetadata | This option determines whether decorator metadata is produced in the JavaScript code emitted by the compiler. |
experimentalDecorators | This option determines whether decorators are enabled. |
importHelpers | This option determines whether helper code is added to the JavaScript to reduce the amount of code that is produced overall. |
lib | This option selects the type declaration files the compiler uses. |
module | This option determines the style of module that is used. |
moduleResolution | This option specifies how modules are resolved. |
outDir | This option specifies the directory in which the JavaScript files will be placed. |
sourceMap | This option determines whether the compiler generates source maps for debugging. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
typeRoots | This option specifies the root location that the compiler uses to look for declaration files. |
For this chapter, I continue working with the angularapp project started in Chapter 17. No changes are required to prepare for this chapter. Open a new command prompt, navigate to the angularapp folder, and run the command shown in Listing 18-1 to start the web service and the Angular development tools.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.

Running the example application
Name | Description |
|---|---|
/products | This URL will display the ProductList component defined in Chapter 17. |
/order | This URL will display the OrderDetails component, defined in Listing 18-2. |
/summary | This URL will display a summary of an order once it has been sent to the server. The URL will include the number assigned to the order so that an order whose ID is 5 will be displayed using the URL /summary/5. |
/ | The default URL will be redirected to /products so the ProductList component is shown. |
This method uses the DataSource to send the user’s order to the server, waits for the response, and then uses the Router object’s navigateByUrl method to navigate to the URL that will display the summary to the user.
The routerLink directive is part of the Angular routing feature and allows navigation without the need to use a Router object in the component class.
The template displays the value of the id property, which is obtained from the active route, and presents a button element that will navigate to the /products URL when clicked.
Save the changes and wait while the development tools rebuild the application and reload the browser. The example application is complete, so you will be able to select products, see a summary of an order, and send it to the server, as shown in Figure 18-2.
If only the browser URL changes when you click the Submit Order button, the likely reason is that you did not replace the contents of the app.component.html file as shown in Listing 18-7.

Adding components to the example application
The Angular development tools rely on the Webpack Development Server, which is not suitable for hosting a production application because it adds features such as automatic reloading to the JavaScript bundles it generates. In this section, I work through the process of preparing the Angular application for deployment, which is a similar process for any web application.
For production, a regular HTTP server is required to deliver the HTML, CSS, and JavaScript files to the browser. For this example, I am going to use the Express server, which is the same package I use for all the examples in this part of the book and is a good choice for any web application. Use Control+C to stop the Angular development tools and use the command prompt to run the command shown in Listing 18-9 in the angularapp folder to install the express package.
The statements in the server.js file configure the express and json-server packages to serve the content of the dist/angularapp folder, which is where the Angular build process will put the application’s JavaScript bundles and the HTML file that tells the browser to load them. URLs prefixed with /api will be handled by the web service.
The URLs in Listing 18-12 are specified relative to the one used to request the HTML document, following the common convention that data requests are prefixed with /api.

Running the production build
To complete this chapter, I am going to create a Docker container for the Angular application so that it can be deployed into production. If you did not install Docker in Chapter 15, then you must do so now to follow the rest of the examples in this chapter.
The devDependencies section specifies the packages required to run the application in the container. All of the packages for which there are import statements in the application’s code files will have been incorporated into the bundle created by webpack and are listed. The other fields describe the application, and their main use is to prevent a warning when the container is created.
The contents of Dockerfile use a base image that has been configured with Node.js and that copies the files required to run the application into the container, along with the file that lists the packages required for deployment.
An image is a template for containers. As Docker processes the instructions in the Docker file, the NPM packages will be downloaded and installed, and the configuration and code files will be copied into the image.

Running the containerized application
The Angular application is ready to deploy to any platform that supports Docker.
In this chapter, I completed the example Angular application by adding components and using the URL routing feature to specify when they will be shown to the user. I prepared the production build of the application and containerized it so that it can be easily deployed. In the next chapter, I create a web application using the React framework.
Name | Description |
|---|---|
allowJs | This option includes JavaScript files in the compilation process. |
allowSyntheticDefaultImports | This option allows imports from modules that do not declare a default export. This option is used to increase code compatibility. |
esModuleInterop | This option adds helper code for importing from modules that do not declare a default export and is used in conjunction with the allowSyntheticDefaultImports option. |
forceConsistentCasingInFileNames | This option ensures that names in import statements match the case used by the imported file. |
isolatedModules | This option treats each file as a separate module, which increases compatibility with the Babel tool. |
lib | This option selects the type declaration files the compiler uses. |
module | This option determines the style of module that is used. |
moduleResolution | This option specifies the style of module resolution that should be used to resolve dependencies. |
noEmit | This option prevents the compiler from emitting JavaScript code, with the result that it checks code only for errors. |
resolveJsonModule | This option allows JSON files to be imported as though they were modules. |
skipLibCheck | This option speeds up compilation by skipping the normal checking of declaration files. |
strict | This option enables stricter checking of TypeScript code. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
React projects are most easily created using the create-react-app package. Open a new command prompt, navigate to a convenient location, and run the command shown in Listing 19-1 to install the create-react-app package.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
The --template typescript argument tells the create-react-app package to create a React project that is configured for use with TypeScript, which includes installing and configuring the TypeScript compiler and the declaration files that describe the React API and its related tools. The --use-npm command installs the packages using the NPM package manager, which I have used throughout this book.
See https://create-react-app.dev/docs/adding-typescript for details of how to add TypeScript to an existing React project.
Once the creation process is complete, run the commands shown in Listing 19-3 to navigate to the project folder, add the packages that will provide the web service, and allow multiple packages to be started with a single command.

Running the example application
TypeScript is optional when using React, and this is reflected in the way that the development tools and the TypeScript compiler are configured. Behind the scenes, the webpack and Webpack Development are used to create the JavaScript bundle and deliver it to the browser.

The JavaScript React development toolchain
The Babel plugin responsible for JSX plays the same role as the JSX factory class I created in Chapter 15 and replaces the HTML fragments with JavaScript statements, albeit using the more sophisticated and efficient React API. The transformation produces pure JavaScript, which is bundled into a file so that it can be received and executed by the browser. The bundle also includes JavaScript code to unpack any CSS or image resources that the application requires.
The setting worth noting is noEmit. When the noEmit setting is true, the TypeScript compiler won’t generate JavaScript files. The reason for the unusual compiler setting is that it is the Babel package—and not the TypeScript compiler—that is responsible for transforming TypeScript code into JavaScript. The React toolchain includes a Babel plugin that transforms TypeScript into pure JavaScript.

The TypeScript React development toolchain
The noEmit setting makes sense in this context since the TypeScript compiler doesn’t need to create JavaScript files to perform its type checks.
The limitation of this approach is that Babel can’t deal with every TypeScript feature, although there are surprisingly few limitations. At the time of writing, enums are not fully supported, and the namespace feature cannot be used (namespaces are a deprecated forerunner of JavaScript modules and not covered in this book).
You may have received a warning when starting the development tools that warned you of a mismatch between TypeScript versions. This warning reflects the possible difference between the type checking features implemented by the latest TypeScript compiler and the way the TypeScript code is translated into JavaScript by Babel. For a simple project like this one, there are unlikely to be serious issues, but you should consider using only the TypeScript versions that are explicitly supported by the create-react-app package.
The Babel transformation can deal with the spread operator without needing a configuration change, and the effect of the target setting in Listing 19-9 only prevents the TypeScript compiler from generating errors.
This is the same set of data types used for the other web applications in this part of the book. Regardless of which framework you use, the same set of features can be used to describe data types.
React uses the JSX format to allow HTML elements to be defined alongside JavaScript code, similar to the approach that I used when creating the stand-alone web application. During compilation, the HTML elements are transformed into JavaScript statements that use the React API to efficiently display content to the user, a much more elegant approach than the one I created in Chapter 15.
The key building block in a React application is the component that is responsible for generating HTML content. Components are configured using props; they can respond to user interaction by handling events triggered by the HTML elements they render and can define local state data.
The generic type arguments allow the TypeScript compiler to check the component when it is applied so that only properties defined by the Props interface are used and to ensure that updates are applied only to properties defined by the State interface.
The TypeScript compiler will ensure that the right type of event is handled and that updates through the setState method are of the right type and update only the properties defined by the State type.
The result of the component’s function is the HTML that should be displayed to the user and that is defined using the same combination of elements and expressions that class-based components produce from their render method.
The use of separate properties and functions ensures that all changes to state data trigger the React update process, and the TypeScript compiler checks the values passed to the function to ensure they correspond to the generic type argument provided to the useState function.
The choice between function and class components is a matter of personal preference, and both are fully supported by React. I tend to use classes because that’s the programming model that I am most used to, but both approaches have their merits and can be freely mixed in a project.
Components are applied using custom HTML elements whose tag matches the component class name. Components are configured using props, which can be used to provide data or callback functions, just as in Chapter 15 when I created a custom JSX implementation. The ProductList component provides its functionality by composing the Header, CategoryList, and ProductItem components, each of which is configured using the props the ProductList component receives or its state data.

Testing the product list components
The Redux package includes TypeScript declarations, but an additional package is required for the React-Redux package, which connects React components to a data store.
Redux data stores separate reading data from the operations that change it. This can feel awkward at first, but it is similar to other parts of React development, such as component state data, and it quickly becomes second nature. In Redux data stores, actions are objects that are sent to the data store to make changes to the data it contains. Actions have types and are created using action creator functions. To describe the actions that the data store will support, add a file named types.ts to the src/data folder and add the code shown in Listing 19-18.
There are many different ways to create and configure a data store and connect it to React components. In this chapter, I have taken the simplest approach and handled the HTTP requests that interact with the web service in a separate class. What’s important in this section is not how I use the datastore but how I can use TypeScript annotations to describe the approach I have selected to the compiler so that type checks can be performed.
The StoreData interface describes the data that the data store will manage, which, for the example application, defines products and order properties.
The ACTIONS enum defines a set of values, each of which corresponds to an action that the data store will support. Each enum value is used as a type argument to the Action type, which is an interface provided by the Redux package. The Action interface is extended to describe the characteristics of the object for each action type, some of which have a payload property that provides the data that will be required to apply the action. The StoreAction type is the intersection of the action interfaces.
When the function is invoked, it identifies the action using the type property inherited from the Action interface, and it updates the data using the payload property for those actions that provide it. The reducer function will also be invoked when the datastore is first created, which provides an opportunity to define the initial data the application will use.
This file uses the Redux createStore method to create a datastore object, which is exported so that it can be used throughout the application.
The connection process maps data properties from the data store and maps action creators to the component’s props, producing a component that is configured partly by the props used when it is applied as an HTML element and partly from the data store. In the listing, the products, categories, and order props are mapped to the datastore products and order properties, and the addToOrder prop is mapped to the modifyOrder action creator. The result is a component named ConnectedProductList that connects the ProductList component to the data store.
Notice that I have not used type annotations when mapping the component. There are types available, but they become convoluted, and I prefer to let the compiler infer the types and warn me only if there is a problem.

Using a data store
In this chapter, I started a React project that uses TypeScript. I explained the unusual developer tools configuration and the effect it has on the TypeScript compiler configuration. I created React components that are defined using TypeScript features and connected them to a simple Redux data store. In the next chapter, I complete the development of the React project and prepare the application for deployment.
Name | Description |
|---|---|
allowJs | This option includes JavaScript files in the compilation process. |
allowSyntheticDefaultImports | This option allows imports from modules that do not declare a default export. This option is used to increase code compatibility. |
esModuleInterop | This option adds helper code for importing from modules that do not declare a default export and is used in conjunction with the allowSyntheticDefaultImports option. |
forceConsistentCasingInFileNames | This option ensures that names in import statements match the case used by the imported file. |
isolatedModules | This option treats each file as a separate module, which increases compatibility with the Babel tool. |
lib | This option selects the type declaration files the compiler uses. |
module | This option determines the style of modules that are used. |
moduleResolution | This option specifies the style of module resolution that should be used to resolve dependencies. |
noEmit | This option prevents the compiler from emitting JavaScript code, with the result that it only checks code for errors. |
resolveJsonModule | This option allows JSON files to be imported as though they were modules. |
skipLibCheck | This option speeds up compilation by skipping the normal checking of declaration files. |
strict | This option enables stricter checking of TypeScript code. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
In this chapter, I continue to work with the reactapp project started in Chapter 19. Open a command prompt, navigate to the reactapp folder, and run the command shown in Listing 20-1 to start the web service and the React development tools.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.

Running the example application
Name | Description |
|---|---|
/products | This URL will display the ProductList component defined in Chapter 19. |
/order | This URL will display a component that displays details of the order. |
/summary | This URL will display a summary of an order once it has been sent to the server. The URL will include the number assigned to the order so that an order whose ID is 5 will be displayed using the URL /summary/5. |
/ | The default URL will be redirected to /products so the ProductList component is shown. |

Adding URL routing
The NavLink component produces an anchor element (an element whose tag is a) that navigates to a specified URL when it is clicked. The Bootstrap classes applied to the NavLink give the link the appearance of a button.
In Chapter 19, I created a connector for an existing component so that it would receive props that are linked to the data store. In Listing 20-5, I have created a component that is always connected to the data store, which avoids the need to define a separate connector but does mean that the component can’t be used when the datastore isn’t available, such as in another project. This component uses a NavLink to return the user to the /products button and invokes a function prop when the user is ready to send the order to the web service.
The Summary component only needs to know the number assigned by the web service to the user’s order, which it obtains from the current route. The routing package provides details of the route through props, following the established React pattern. The type declarations for the React Router package are used to describe the parameter that the component expects, allowing the TypeScript compiler to check types.
The Route component for the OrderDetails component uses the render function to select the component and provide it with a mix of props provided by the routing system and a callback function. The submitCallback method requires access to the routing features that are provided as props to components to navigate to a new URL, but these are available only within the Browser router component. To work around this limitation, I provide the OrderDetails component with an inline function that passes the routing props to the submitCallback method, which allows the history.push method to be used. The Route component for the Summary component defines a URL with a parameter that provides the order number to display to the user.

Completing the example application
The React development tools rely on the Webpack Development Server, which is not suitable for hosting a production application because it adds features such as automatic reloading to the JavaScript bundles it generates. In this section, I work through the process of preparing the application for deployment, which is a similar process for any web application, including those developed using other frameworks.
For production, a regular HTTP server is required to deliver the HTML, CSS, and JavaScript files to the browser. For this example, I am going to use the Express server, which is the same package I use for the other examples in this part of the book and which is a good choice for any web application. Use Control+C to stop the development tools and use the command prompt to run the command shown in Listing 20-8 in the reactapp folder to install the express package.
The statements in the server.js file configure the express and json-server packages so they use the contents of the build folder, which is where the React build process will put the application’s JavaScript bundles and the HTML file that tells the browser to load them. URLs prefixed with /api will be handled by the web service.
The URLs in Listing 20-11 are specified relative to the one used to request the HTML document, following the common convention that data requests are prefixed with /api.

Running the production build
To complete this chapter, I am going to create a Docker container for the example application so that it can be deployed into production. If you did not install Docker in Chapter 15, then you must do so now to follow the rest of the examples in this chapter.
The devDependencies section specifies the packages required to run the application in the container. All of the packages for which there are import statements in the application’s code files will have been incorporated into the bundle created by webpack and are listed. The other fields describe the application, and their main use is to prevent warnings when the container is created.
The contents of the Dockerfile use a base image that has been configured with Node.js and that copies the files required to run the application into the container, along with the file that lists the packages required for deployment.
An image is a template for containers. As Docker processes the instructions in the Docker file, the NPM packages will be downloaded and installed, and the configuration and code files will be copied into the image.

Running the containerized application
The React application is ready to deploy to any platform that supports Docker.
In this chapter, I completed the React application by adding support for URL routing and by defining the remaining components. As with the other examples in this part of the book, I prepared the application for deployment and created a Docker image that can be readily deployed. In the next chapter, I create the same web application using Vue.js and TypeScript.
Name | Description |
|---|---|
allowSyntheticDefaultImports | This option allows imports from modules that do not declare a default export. This option is used to increase code compatibility. |
baseUrl | This option specifies the root location used to resolve module dependencies. |
esModuleInterop | This option adds helper code for importing from modules that do not declare a default export and is used in conjunction with the allowSyntheticDefaultImports option. |
importHelpers | This option determines whether helper code is added to the JavaScript to reduce the amount of code that is produced overall. |
jsx | This option specifies how HTML elements in TSX files are processed. |
lib | This option selects the type declaration files the compiler uses. |
module | This option determines the style of module that is used. |
moduleResolution | This option specifies the style of module resolution that should be used to resolve dependencies. |
paths | This option specifies the locations used to resolve module dependencies. |
skipLibCheck | This option speeds up compilation by skipping the normal checking of declaration files. |
sourceMap | This option determines whether the compiler generates source maps for debugging. |
strict | This option enables stricter checking of TypeScript code. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
types | This option specifies a list of declaration files to include in the compilation process. |
Vue.js projects are most easily created using the Vue Cli package, which has built-in support for creating Vue.js projects that include TypeScript support. Open a command prompt and run the command shown in Listing 21-1 to install the Vue Cli package.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.
The first @ character is part of the package name, @vue-cli. The second @ character is the separator between the package name and the version that is required, 4.5.11.
Question | Answers |
|---|---|
Please pick a preset. | Manually select features. |
Check the features needed for your project. | Select these features: Babel, TypeScript, Router, Vuex. Do not select the linter. Once you have chosen the features shown, select the Choose Vue version option and select Vue 3.x. (Support for Vue.js 3 is in preview at the time of writing.) |
Use class-style component syntax? | N |
Use Babel alongside TypeScript? | Y |
Use history mode for router? | Y |
Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? | In dedicated config files |
Save this as a preset for future projects? | N |
Pick the package manager to use when installing dependencies | Use NPM. |
Once you have answered the questions, the project will be created, and the packages it requires will be installed.
These entries allow both the web service that will provide the data and the Vue.js development tools to be started with a single command.
TypeScript isn’t required for Vue.js development, but it has become such a popular choice that the main Vue.js packages contain complete type declaration files, and the Vue Cli package can create projects ready-configured for TypeScript.
This value specifies TypeScript and ensures that the code will be processed by the TypeScript compiler. Components are defined using the defineComponent function and are expressed using the JavaScript object literal syntax. There is support for defining components using classes, but I have not used that feature because its future seems uncertain and the literal syntax is more widely used.

The Vue.js toolchain
These types describe products and orders and the relationship between them. Unlike the other chapters in this part of the book, Product is defined as a class and not a type alias, because the Vue.js development tools rely on concrete types. The Vue.js change detection system doesn’t work well with the JavaScript Map, so the Order class for this chapter is written using an array for storage.
Vue.js supports different ways of defining components, which are the key building block for displaying content to the user. For this book, I am going to use the most popular, which is the single-file component format that combines HTML and its supporting code in one file. (These files can also contain CSS, but I won’t be using that feature since I am relying on the Bootstrap package configured in Listing 21-6.)
A Vue.js component’s template element uses data bindings, denoted by double curly brackets ({{ and }}), to display data values and uses event handling attributes, prefixed by the @ character, to handle events. The expressions specified by the bindings and the event attributes are evaluated using the featured defined by the class in the script element.
This component in Listing 21-10 displays the details of a Product object and emits an event when the user clicks the Add To Cart button.
Vue.js implements simple type checking for props, and to accommodate TypeScript, this expression uses the PropType<T> generic type, where the expected type is specified as the type argument. In this case, the expression specifies that the expected type of the product prop is Product.
The data property is assigned a function that returns an object used to define the state data that the component requires. This component defines a single state data property named quantity, which has an initial value of 1.
The result is that clicking the button causes the component to trigger an event that will be received by its parent. The data sent with the event includes the Product object received as a prop and the current value of the quantity state data value.
This v-for directive tells Vue.js to create a button element for each value returned in the sequence returned by the categories property. To perform efficient updates, Vue.js requires a key attribute to be assigned to each element, which is why v-for and v-bind:key are used together.
The result is a series of button elements for each category. Clicking the button invokes the selectCategory method, which triggers a custom event and allows a component to signal the user’s category selection to another part of the application.
Without the required property, the TypeScript compiler will set the type of the categories prop to string[] | undefined, which would then require the expression that sets the selected property to check for undefined values to prevent compiler errors.
The Header component displays a summary of the current order. The computed property is used to define functions whose result is derived from the component’s data, including its props. This allows Vue.js to cache the values produced by these functions and invoke the functions only when the component’s data changes. This Header component defines a computed function named displayText, whose result depends on its order prop.
The Header element applies the Header component. Vue.js uses the v-bind directive to create a data binding that sets the Header component’s order prop to the order property defined by the ProductList component, allowing one component to provide data values to another.

Testing the product list components
Vuex data stores are set up with four properties: state, mutations, actions, and modules. The state property is used to set up the state data managed by the data store, the mutations property is used to define functions that modify the state data, and the actions property is used to define asynchronous tasks that use mutations to update the store. The modules property is used to manage complex data stores that are defined in multiple files, but I don’t use this feature for this example application.
The state data properties defined by the store can be accessed using store.state.
The spread operator ensures that the properties produced by the mapping functions are incorporated into the component’s features.
In the component’s template, I have updated the Header element because the component no longer receives an Order object as a prop; now it is connected to the user store.

Using a data store
Actions can modify the data store only through mutations. The changes in Listing 21-18 define actions that allow products to be loaded and added to the store and that allow orders to be sent to the server.

Using the web service
In this chapter, I showed you how to create a Vue.js app that uses TypeScript. The project creation package provides integrated support for TypeScript, which I used to create the basic structure of the application. I connected the application’s components to a Vuex data store and loaded data from the web service. In the next chapter, I complete the Vue.js web project and prepare the application for deployment.
Name | Description |
|---|---|
allowSyntheticDefaultImports | This option allows imports from modules that do not declare a default export. This option is used to increase code compatibility. |
baseUrl | This option specifies the root location used to resolve module dependencies. |
esModuleInterop | This option adds helper code for importing from modules that do not declare a default export and is used in conjunction with the allowSyntheticDefaultImports option. |
importHelpers | This option determines whether helper code is added to the JavaScript to reduce the amount of code that is produced overall. |
jsx | This option specifies how HTML elements in TSX files are processed. |
lib | This option selects the type declaration files the compiler uses. |
module | This option determines the style of module that is used. |
moduleResolution | This option specifies the style of module resolution that should be used to resolve dependencies. |
paths | This option specifies the locations used to resolve module dependencies. |
skipLibCheck | This option speeds up compilation by skipping the normal checking of declaration files. |
sourceMap | This option determines whether the compiler generates source maps for debugging. |
strict | This option enables stricter checking of TypeScript code. |
target | This option specifies the version of the JavaScript language that the compiler will target in its output. |
types | This option specifies a list of declaration files to include in the compilation process. |
In this chapter, I continue to work with the vueapp project started in Chapter 21. Open a command prompt, navigate to the vueapp folder, and run the command shown in Listing 22-1 to start the web service and the React development tools.
You can download the example project for this chapter—and for all the other chapters in this book—from https://github.com/Apress/essential-typescript-4.

Running the example application
Name | Description |
|---|---|
/products | This URL will display the ProductList component defined in Chapter 21. |
/order | This URL will display a component that displays details of the order. |
/summary | This URL will display a summary of an order once it has been sent to the server. |
/ | The default URL will be redirected to /products so the ProductList component is shown. |

Using URL routing
The router-link element renders an HTML element that navigates to the specified URL when it is clicked and that is styled to appear as a button using the Bootstrap CSS framework.
The OrderDetails component uses the Vuex mapActions function to create a method that invokes the storeOrder action. It also defines a method named submit that invokes the mapped storeOrder method, uses the HttpHandler class to send the Order to the web service, and redirects to the /summary URL.
The Summary component only needs to know the number assigned by the web service to the user’s order, which it obtains from the data store. The router-link element allows the user to return to the /products URL.

Completing the example application
The Vue.js development tools rely on the Webpack Development Server, which is not suitable for hosting a production application because it adds features such as automatic reloading to the JavaScript bundles it generates. In this section, I work through the process of preparing the application for deployment, which is a similar process for any web application, including those developed using other frameworks.
For production, a regular HTTP server is required to deliver the HTML, CSS, and JavaScript files to the browser. For this example, I am going to use the Express server, which is the same package I use for the other examples in this part of the book and which is a good choice for any web application. Use Control+C to stop the development tools and use the command prompt to run the command shown in Listing 22-8 in the vueapp folder to install the express package.
The statements in the server.js file configure the express and json-server packages so they use the content of the dist folder, which is where the Vue.js build process will put the application’s JavaScript bundles and the HTML file that tells the browser to load them. URLs prefixed with /api will be handled by the web service.
The URLs in Listing 22-11 are specified relative to the one used to request the HTML document, following the common convention that data requests are prefixed with /api.
The build process creates a set of optimized files in the dist folder. The build process can take a few moments to complete.

Running the production build
To complete this chapter, I am going to create a Docker container for the example application so that it can be deployed into production. If you did not install Docker in Chapter 15, then you must do so now to follow the rest of the examples in this chapter.
The devDependencies section specifies the packages required to run the application in the container. All of the packages for which there are import statements in the application’s code files will have been incorporated into the bundle created by webpack and are listed. The other fields describe the application, and their main use is to prevent warnings when the container is created.
An image is a template for containers. As Docker processes the instructions in the Docker file, the NPM packages will be downloaded and installed, and the configuration and code files will be copied into the image.

Running the containerized application
The React application is ready to deploy to any platform that supports Docker.
In this chapter, I completed the Vue.js and TypeScript project and prepared the application for deployment into a Docker container. Each of the web applications created in this part of the book shows a different approach to integrating TypeScript into the development process and emphasizes different TypeScript features. The result, however, has been the same: an improved developer experience that can improve productivity and help avoid common JavaScript errors.
And that is all I have to teach you about TypeScript. I started by creating a simple application and then took you on a comprehensive tour of the different features that TypeScript provides and how they are applied to the JavaScript type system. I wish you every success in your TypeScript projects, and I can only hope that you have enjoyed reading this book as much as I enjoyed writing it.