You are on page 1of 101

Table

of Contents
Introduction 1.1
Chapter 1: Road Map 1.2
Chapter 2: The Gloss Overs 1.3
Chapter 3: Using TypeScript - A Practical Overview 1.4
Chapter 4: Putting the Type in TypeScript 1.5
Chapter 5: Types in Depth 1.6
Chapter 6: Template Strings 1.7
Chapter 7: Functions 1.8
Chapter 8: Introducing Classes 1.9
Chapter 9: Classes in Depth 1.10
Chapter 10: Generics 1.11
Chapter 11: Continue Learning 1.12

1
Introduction

The Grand Introduction


Greetings and welcome to the exciting world of TypeScript!

This book aims to provide a casual introduction to the main features of the TypeScript
language. This chapter covers:

The intended audience for this book (i.e. who did I imagine would be interested in
reading it?)
Why learn TypeScript? What's the return on investment here?
Aren't there enough books out there already? Why write another one?
A more personal note on how I wrote the book for those of you interested in the
mechanics and other metrics of such things.

Version
The current version of this book is: 1.0.2. Last pushed on 07/11/2017.

See the end of this chapter for a more detailed version history.

Intended Audience
This book is meant for three types of users:

You've never used TypeScript, you're not sure you ever want to use it, but you've been
hearing a lot about it on the Social Media. You want a hassle-free introduction.
You've been dabbling with TypeScript you're mostly sold on it. You want to dig a bit
deeper into mainstream TypeScript "stuff" like interfaces, classes and generics. You
want a book that does this in a structured manner that also doesn't overwhelm you with
the more obscure or advanced bits like intersection types, mix-ins or decorators.
You're a Java or C# programmer who wants to transition to the world of front-end
development but have always avoided it because you don't like that oily feeling you get
when working with plain JavaScript. Duck typing just isn't your cup of tea.

If you fit any of these categories, welcome!

If not, you're welcome, too!

2
Introduction

Before we continue, if you just want to get started learning the language then skip ahead to
chapter 3. This chapter is your typical fluffy introduction and chapter 2, The Gloss Overs,
actually identifies out of scope topics. Things start to get interesting and, well, practical, in
chapter 3, Practical Considerations.

Introduction - Why TypeScript?


The JavaScript community's ranks are growing, have been growing and will probably
continue to grow in the forseeable future. Why is that? There are a lot of reasons. The
entrance fee is quite low - all you need is a text editor and web browser. If you have a
reasonably modern computer, you can start writing JavaScript apps. Newly minted
developers can quickly build JavaScript solutions with minimal tooling and they don't need
extravagant training1 beforehand. Some experienced server-side developers see a
comparatively easy edit/save/deploy process and want to join the band up on this wagon.
The language is nearly ubiquitous. JavaScript is just about everywhere these days -
browsers, servers (via node) and is helping to power the emerging field of the Internet of
Things (IoT). It's even making a play to displace, or at least take a seat at native
applications' table2. JavaScript is, effectively, the machine language of the internet.

As I write these words, the JavaScript development community continues to grow in size. Its
tooling options are more numerous than ever before. Great tooling affords the community
the opportunity to build truly amazing solutions on the front and back end. The opportunity
landscape expands every day. This is an age where we lucky few get to apply our intellect to
some fascinating problem domains. These are exciting times!

We're also inundated with options. We have full blown frameworks like Angular, React,
Ember, Meteor, Vue and others. We have sophisticated task runners and bundlers such as
gulp, grunt and webpack. We have classic go-to utility libraries such as JQuery, lodash and
underscore.

This begs the question, why should we further complicate our lives and take time to learn
about TypeScript if JavaScript already has all the things?

I believe there are two good reasons to look at TypeScript - its growing popularity and - more
importantly - the pure joy of working with it.

TypeScript's Popularity
For starters, at least one major framework, Angular 2+, pushes you hard into using
TypeScript. If you work at an Angular shop, you have a strong practical reason to learn it3.
Other frameworks provide TypeScript-friendly features or vice-versa. For instance,

3
Introduction

TypeScript provides first-class support to React developers via smart JSX intellisense.

Some poeple might characterize "learn it because you need it for future dev work" as a
"negative externality" and not exactly popular. That would be fair. But consider the fact that
many pure JS library authors have worked hard to provide TypeScript-friendly interfaces.
Here's the DefinitelyTyped github project:

Check it out today and see how much it's grown since March 2017.

At a high level, typings help bridge the gap between TypeScript and pure JavaScript
libraries. I bring up the DefinitelyTyped github project because it shows that a good-sized
group of library developers and/or devotees think it's worth their time to create these typings.
As you can see in the screen capture above, with over 3,000 contributors, almost 27k
commits and pushing 10k stars - the project offers some evidence that it's worth your while
learning about this.

All of this is to say that TypeScript has and continues to gain traction in the JavaScript
community. The jury is still out as to whether it will "win" and become a truly main stream
scripting language for web dev. It probably won't become the language of the web4. But its
appeal cannot be denied. A growing swath of current and future front end developers are
choosing TypeScript over pure JavaScript. Some folks are even wondering if they should
learn TypeScript alone and skip JavaScript altogether.

The Pure Joy Of It


TypeScript's growing popularity answers the "Why TypeScript" question in part. A lot of us
aren't swayed by popularity, however. I'm sympathetic to that point of view.

I came to TypeScript after spending about three or four years building solutions that
contained minor to major bits of client-side code. I used jQuery for DOM manipulation and
async work and lots of straight JS logic. I struggled with all the usual things at first, like
closures or how to test for null or undefined and the family of "=", "==" "===" operators. I
eventually came to an uneasy understanding of these things but some problems never went
away. I found it difficult to apply common design patterns with great confidence. Pure
JavaScript is terribly difficult to refactor, even for trivial changes.

4
Introduction

As you'll discover in chapter 4, TypeScript is a statically typed language. Or at least, you can
use types if you want. It's much less ambiguous than pure JavaScript both from an "intent"
perspective ("what did the programmer intend with this code?") as well as straight-up
technical meaning. Since TypeScript mitigates ambiguity, IDEs can provide better support.
You'll see many examples of this throughout the book.

These two points combine to make working with TypeScript a truly joyful experience.
Closure complexities recede. It's more difficult to accidentally assign strings to numbers.
Refactoring isn't as difficult so you're likely to make necessary changes in a timely manner
rather than pile on a little more technical debt. It's a breeze to manage complex objects.
TypeScript's syntax maps nicely and neatly to many common design patterns.

For me, TypeScript is like starting to cook a large, complex meal in a freshly cleaned, well
organized and well-stocked kitchen. You can focus on producing a beautiful meal instead of
worrying over whether you have enough unexpired tarragon in your spice rack.

It is, simply, a joy.

Why Another TypeScript Book?


The market has given rise to TypeScript several books, free and not-free (not to mention
thousands of blog posts, paid articles, videos and PluralSight courses). This hasn't stopped
me from writing this one because I believe the market can bear another one5. I also know
that different people learn things differently - more teaching "voices" help more people. This
book is one such voice. I hope you find it pleasant and more importantly, helpful :).

At the moment, there are several great resources for learning TypeScript, including the
proto-iconic. TypeScript Deep Dive, which you can read here:
http://basarat.gitbooks.io/typescript/.

Some chapters list out a few "further investigation" links, mostly to blog posts. The final
chapter lists all of them all over again included links to advanced topics that are not
(currently) covered in the book. They ought to help you in your journey.

About the Book


I'm always a bit curious about how books like this come into being and thought I'd share a bit
about my process for those of you that may be interested.

I spent about six months writing the book, including two periods (each about 3 weeks) of
fairly intensive writing and many weeks with no writing. I tend to write a lot of high level bullet
points and then go off and do something completely unrelated, like an out-of-control home
6

5
Introduction

renovation project6. Meanwhile, things are percolating in the back of my head and when I sit
down to write, a good bit gets out fairly quickly.

I decided to write this book using VS Code instead of a more traditional text editor, such as
MS Word. VS Code isn't a word processor, but it does support markdown. It integrates nicely
with Git. I wrote a few thousand words and found that markdown is plenty good enough for
what I want, especially since you can inject plain HTML right inline with the text. That's how
my callout boxes work and how I embed videos.

About the videos - I have come to learn over the years the value of "differentiated
instruction," which is a term of art in the teaching profession. It just describes a process
where you teach the same concept two or more different ways. Some people respond well to
the first and others to the second.

The book's videos don't have any sound. I didn't want to introduce any barriers, like plugging
in ear phones, to view them. They contain many annotations and you can and should
obviously pause and rewind as necessary.

Contributing to this Book


I think that most authors, and I count myself among them, derive immeasurable satisfaction
from reader feedback. If you'd like to contribute to the book in a non-material, spiritual way
(like "attaboy!" or "Dear Lord, what fresh hell have you visited upon the world with this
book!"), the easiest thing is to simply send me a note to galvin.paul@gmail.com. It would be
helpful if you put the words "TypeScript Book" in the subject, but certainly isn't required. I
always get a little extra pep in my step when someone leaves a comment on one of my blog
posts or reaches out by email. It's better than being paid7.

I have long been impressed, interested and even a bit envious of the You Don't Know
JavaScript series. Kyle Simpson obviously hit a nerve and he has a really thriving Github
project going. I have, in fact, tried to follow his model. If you'd like to participate in a more
material way, hit up this book's github site, https://github.com/pagalvin/tsbook:

Star the project


Log some issues
Correct problems you find and issue a pull request
Suggest and even write entire new areas of content and issue a pull request

I will make every effort possible to respond to your emails, review and manage github issues
and honor high quality pull requests.

6
Introduction

Further Reading
This chapter's "further reading list" is much more extensive than any other. These articles
reflect TypeScript's growing popularity, as well as at least one "anti-TypeScript" article.

Here they are:

Moving off Flow to TypeScript: http://jan.varwig.org/2017/02/15/flow-vs-typescript.html


Preferring Flow over TypeScript for earlier error detection:
http://thejameskyle.com/adopting-flow-and-typescript.html
Stack Overflow tackling the question, "Why use TypeScript over JavaScript?"
https://stackoverflow.com/questions/12694530/what-is-typescript-and-why-would-i-use-
it-in-place-of-javascript
Short introduction to TypeScript for C# Developers:
http://www.codeguru.com/csharp/csharp/cs_webservices/tutorials/typescript-for-the-c-
developer.html
The good people at Slack (team messaging platform) migrated their code base to
TypeScript. This article explains why: https://slack.engineering/typescript-at-slack-
a81307fa288d
Still unconvinced? Maybe this author's "Six Reasons" article will convince you:
https://www.siliconrepublic.com/enterprise/typescript-programming-javascript
An old "anti-TypeScript" article from 2014. It's actually a bit hard to find "TS is Bad"
articles, but I wanted to include at least one thoughtful example and this is it:
http://walkercoderanger.com/blog/2014/02/typescript-isnt-the-answer/
Another software dev team explaining their decision to use TypeScript:
https://medium.com/@tomdale/glimmer-js-whats-the-deal-with-typescript-f666d1a3aad0

Copyright
© 2017 Paul Galvin.

Version History

7
Introduction

Date Version Change

Initial go-live of the book, begin "marketing" it on social


07/07/2017 1.0
media.
07/08/2017 1.01 Added a link by Ayo Alfonso to chapter 4, Introducing Types
Added Navalia github project to Continue Learning chapter.

Updated TOC with chapter numbers.


Merged a pull request from Frank Ali fixing a mistake in an
07/11/2017 1.02 example union type ("up" instead of "right"). Still need to
correct the associated video.

Moving On
That's it's for the fluffies. The next chapter discusses some out of scope topics so that you
don't get your hopes up for things not covered here in the book.

1. That's not to say that they are doing it well, but they are surely giving it a try. ↩

2. Electron, for instance, provides a way to create and package JavaScript applications

to be run on the desktop. Electron itself is very TypeScript-friendly. ↩


3. Even if you're an Angular 1.x shop today, you'll want to try and get ahead of the curve

by learning TypeScript. It's only a matter of time before your shop starts investing in
Angular 2+. ↩
4
. Given that JavaScript is already the "machine language of the web," we riches-
embarrassed developers will be able to pick almost any language we want to use in the
future and work seamlessly with everyone else. See more of my thoughts on this
subject here: "The Wonderful Consequences of JavaScript as the Virtual Machine
Language of the Web" ↩
5. At least I think the market can bear another free, online book :). ↩

6. It started with "hey, let's pull up the carpet and refinish the hardwood!" And then it

progressed, by inches, to re-doing the hardwood in the whole first floor, pull out the old
trim and replacing it, fixing cracks in the ceiling, painting the basement stairs and
putting in new treads, new chandelier and kitchen stuff... It's still not done. Fun stuff, but
... maybe I shouldn't have started. ↩
7. Mostly better than being paid. ↩

8
Introduction

9
Chapter 1: Road Map

The Road Map


This book follows familiar conventions. It addresses a few house keeping topics (like this
very chapter) before properly introducing the language. That proper introduction starts off
with simple topics (variables and data types) and works its way up to more complicated
areas towards the end, such as generics.

The Gloss-Overs
This book won't go into any great detail about some topics. It's mostly concerned with the
TypeScript language. As a result, the book does not provide step-by-step instructions for
things like downloading TypeScript, installing it or configuring it. This and similar topics are
covered in much better detail elsewhere. The final chapter, "Where Do I Go From Here?"
points to some useful online resources focusing on these things.

Practical Considerations
This chapter covers the TypeScript development experience at a higher level. It answers
questions such as:

How do I write TypeScript applications in the first place?


How does TypeScript run in the browser? (It doesn't, actually - it "transpiles" to
JavaScript).
How do I debug TypeScript apps?

The goal here to help ground you in the TypeScript "world" and describe the big picture of
what's happens as you build TypeScript solutions. As you'll see, it's not very complicated1.

Introducing Types
TypeScript offers static types. You don't need to use them, but they are pretty helpful. This
chapter starts off describing primitives (integers, strings and the like). It shows how declaring
a variable's type helps good integrated development environments (IDEs) provide useful
edit-time and compile-time feedback. We'll also take an opportunity to try and knock the
TypeScript doubters off their perch with the strong typing goodness :)2.

10
Chapter 1: Road Map

Types in Depth
The real world is complicated with complex data structures. TypeScript offers up the notion
of interfaces to help us describe and manage them. This chapter introduces interfaces as
a way to describe them starting with a flat object and moving on to a more complex JSON
formatted response from a REST service.

TypeScript interfaces look and feel quite similar to interfaces in C# and Java. Generally
speaking, interfaces are one of the backbones for many common and important design
patterns and principles (think SOLID). TypeScript interfaces enable us to more directly
implement these design patterns3.

TypeScript offers several other ways to describe data. The chapter covers a few the most
useful ones. These include:

Enumerations: Assigning a label to a fixed value.


Union Types: Define a new custom type that can hold two or more different types of
values (including hard coded strings).

TypeScript provides other types, such as intersection types. This book takes a pass on those
types for now - they feel like edge cases and although interesting and vitally useful when you
can, you know, use them, most of us don't live on the edge.

Template Strings
Eliminate cumbersome string manipulation through the magic of template strings!

Functions
A detailed look into how TypeScript enhances standard JavaScript functions, including typed
parameters, void return values, default function parameter values and more.

In addition, learn about Arrow Functions, often called "anonymous" or "lambda" functions.

Introducing Classes and Classes in Depth


TypeScript's static typing is, as they say, the bee's knees. Classes are the honey and these
two chapters cover them pretty thoroughly:

Class syntax
Classes and interfaces

11
Chapter 1: Road Map

Inheritance
Abstract classes
Static class members

Classes are an important building block for object oriented programming and TypeScript
provides some solid support here.

Generics
Learn TypeScripts' version of generics as you know them from C# and Java.

Continue Learning
A big long list of links that will hopefully lead you to TypeScript Greatness.
1. Isn't this always the case? No specific thing in this post-modern JavaScript world of

ours is particularly complicated. It surely adds up though. ↩


2. If you or someone you know is a TypeScript doubter, have a look at this chapter and

its videos. Invest fifteen minutes or so here and then make up your mind about whether
you want to invest more time after that. ↩
3. As they say in the Old Country, "Come for the static typing, stay for the interfaces." ↩

12
Chapter 2: The Gloss Overs

The Gloss-Overs
This book focuses on what consider to be the "main" bits of the TypeScript language. This
narrows its scope and as a result, several topics that you might think a table of contents
informed by common sense would include are not, in fact, included1. To wit:

- History of TypeScript: It's an interesting topic2, but doesn't help


achieve this book's practical aims. If you want to learn mor
History of TypeScript, consult the great people at Wikipedia to
start.

IDEs, deployment processes, webpack and task runners:


This stuff is changing almost all the time, almost on a
weekly basis. If I were to go all in, recommending some
particular "TypeScript starter", it would be overtaken by newer, more streamlined
solutions by the time you read my recommendation. In addition, the various starters you
find on the interwebs tend to be framework-dependent. One family of starter projects
supports Angular, another React, etc. This book isn't about those frameworks.

Beyond those non-language features, the book also ignores what I consider to be more
advanced and/or situational topics. These are:

Decorators. Decorators allow you to enhance the functionality a TypeScript artifact,


such as a class or property. This assignment is done in a declarative manner and can
enhance the targeted artifact without the knowledge or consent of the artifact. It's fairly
meta, I know :). They are very powerful and may become a common thing for people to
do over time. However, I think they are too advanced for my target audience. In the
chapter, Continue Learning, I point out a few great blog posts and github projects that
showcase them.

Modules: Modules are not particularly complicated, but they mostly support bundling
and other tooling (such as webpack). I'm on the fence as to whether I include a chapter
on modules even as I write this. For now, I won't and I will refer you to good overviews
of modules in the Continue Learning chapter.

Mixins, intersection types and the like: I cover what I consider to be the "core" language
features. As with decorators, some topics are fairly advanced while also being of use in
a fairly narrow band of business applications. I didn't want to throw too many concepts
out there. As a result, I leave these out of the book and invite you to learn about them
from other sources.

13
Chapter 2: The Gloss Overs

That's enough on what's not covered. Let's move on to first big topic, how the heck does
TypeScript work in the first place?
1. Of course, we don't necessarily share the same sense of what's common. If you don't

like mine, write your own book :). Or, suggest I add more content via a github issue or
email me directly. The intro chapter provides links to both of these options. ↩
2. Did you know that TypeScript is over five years old? In the U.S., President Barack

Obama was only just about to get re-elected to office when Microsoft released this
language. Coincidence? Who knows? (Photo of Barack Obama from Peter Prodoehl @
https://www.flickr.com/photos/raster/). ↩

14
Chapter 3: Using TypeScript - A Practical Overview

Using TypeScript - A Practical Overview


TypeScript shares something in common with virtually every other computer language - a
TypeScript program is just a text file. Computers can't run it without first transforming that
text to machine language. That process is named compilation, of course, and it isn't exactly
a new thing under the sun1.

These days, many popular languages don't even compile down to


machine language. Java compiles down to bytecode. A virtual machine
interprets that bytecode and executes your application. C# and other
.NET languages compile down to bytecode as well, albeit a different
Me, foolishly scoffing at
format. They run in a different virtual environment, the compile language Microsoft when they first
2 announced .NET.
runtime or CLR) . If you squint your eyes, even web browsers act as
virtual machines3.

All modern web browsers know how to run JavaScript. They don't necessarily all agree upon
the finer points of the DOM. They may vary their behavior with things like the console object.
This and that bit of CSS acts different across different vendors' browsers (and by versions
within the same vendor's browsers4). Despite their differences, however, they agree more
than they disagree5. In most cases the JavaScript I write today runs on every browser on
the planet, or at least the browsers I care about :).

TypeScript isn't JavaScript, of course. It looks a lot like JavaScript and as you'll see, you can
write valid TypeScript that is literally identical to pure JavaScript. However, no major web
browser today can execute TypeScript. There's a gap here. Transpilation bridges the gap
between TypeScript and JavaScript by converting TypeScript to JavaScript.

Here's one way to look at it:

15
Chapter 3: Using TypeScript - A Practical Overview

Figure: High Level TypeScript Transpilation Process

As a practical matter, it's no different from the kind of compilation process we go through
with Java, .NET, Rust and other languages. However, it's usually referred to as a
transpilation process rather than a compilation process. I'll tend to use both words in the
book.

You don't need to own Visual Studio or use VS Community Edition to write TypeScript code.
Many popular IDEs provide a first-class TypeScript experience, including Sublime,
WebStorm and Visual Studio's younger sibling, Visual Studio Code. The JS community has
largely embraced VS Code so it's a safe and easy bet. I have used VS Code to write all of
the examples in this book (and indeed, the book itself) and many of the screen captures
show VS Code in the background. That said, the book is not about VS Code or any other
Integrated Development Environment (IDE). Pick whichever your prefer.

Today's market provides us with one TypeScript compiler. Microsoft created it, they maintain
it and it's open source. As you'd expect, it's well-integrated with Visual Studio and
interestingly, works even better with VS Code. However, it is actually a stand-alone
independent application in its own right. Download and install from here:
https://www.typescriptlang.org/.

A Brief Note on Task Runners


When you use a task runner, you define
individual tasks and you usually "define" them
by writing them in JavaScript. This means that
you can write your tasks in TypeScript too.

16
Chapter 3: Using TypeScript - A Practical Overview

Webpack 2 seem to have the most mind share.

From here on out, I assume that you have selected an IDE and that your IDE is connected to
a task runner that automatically compiles your TypeScript whenever you make a change and
save.

Debugging
When I first started wondering about TypeScript, one of my very first thoughts was - how do I
debug it? It turns out to be a very simple and natural process. There can be some quirks
with web server config because they aren't configured to serve up map files the right way.
These kinds of problems are all solvable. I can't emphasize how much of a nothingburger
debugging concerns become once I understood the process.

Web browsers such as Chrome, Internet Explorer and Firefox provide great debugging tools.
These tools work with plain JavaScript. Here's a video showing some code that iterates over
a collection of objects and the Chrome debugger experience:

(If you can't see this video, try clicking here or type this URL into your web browser:
https://youtu.be/MFwfodjYFoo.)

That looks all well and fine but how does it work with TypeScript?

First, the TypeScript compiler aims to generate human-readable JavaScript. As a result that
transpiled code is often easy to reason about if it's all you have in the debugger. Let's do a
quick video demo.

17
Chapter 3: Using TypeScript - A Practical Overview

First, the TypeScript compiler aims to generate human-readable JavaScript. As a result that
transpiled code is often easy to reason about if it's all you have in the debugger. Let's do a
quick video demo.

(If you can't see this video, try clicking here or type this URL into your web browser:
https://youtu.be/Xb6zREHBGV8).

As you can see, the generated JavaScript is similar to first plain JS adder from above.
However, it is quite different from the actual TypeScript code. It's not so different that it's
impossible to reason about, but it definitely creates some unnecessary friction. Thankfully,
the TypeScript compiler will generate map files for us. These provide a connection between
the actual JavaScript the browser is running and its associated TypeScript source code.

Map files are pretty interesting things in and of themselves. They don't just work for
TypeScript, they work more generally with any JavaScript related source. For instance, in
development situations, you'll often minimize your JavaScript but provide a map file that
maps the minimized source back to its unminified state. Other languages, like ClosureScript,
also use map files. The point here is that map files weren't invented for TypeScript,
TypeScript simply leverages this existing capability. Here's an ancient article that describes
source maps when they were first introduced back in 2012. For you historians this article has
also aged well.

Here's the same experience, but this time using a map file:

18
Chapter 3: Using TypeScript - A Practical Overview

(If you can't see this video, [try clicking here] or type this URL into your web browser:
https://youtu.be/RQEUdV84WJg.)

See, debugging TypeScript really is a nothingburger :).

Further Reading
You may find the following articles helpful:

Light introduction to setting up and using the TypeScript compiler: http://david-


barreto.com/introduction-to-the-typescript-transpiler/
Using Gulp to automate TypeScript builds:
https://wipdeveloper.com/2015/12/12/typescript-compile-with-gulp/?
utm_content=buffer15f75&utm_medium=social&utm_source=twitter.com&utm_campaig
n=buffer
Setting up VS Code to work with TypeScript: http://blog.mannsoftware.com/?p=1951

Summary
We write our TypeScript source using our favorite text editor which itself is usually part of our
favorite IDE. The TypeScript compiler generates plain JavaScript based off our TypeScript
source. This is a good thing, since web browsers know how to run JavaScript.

That generated source is, for the most part, human readable. That gives us a tolerable
debugging experience. However, we get a first class debugging experience by using map
files.

19
Chapter 3: Using TypeScript - A Practical Overview

That's enough big picture background information. It's now finally time introduce static types,
starting with the next chapter.

1. The name of this book itself pays homage to YACC, a compilation tool whose full

name is "Yet Another Compiler Compiler." If you've ever been interested in writing a
compiler (and really, who hasn't heard that siren call?) then YACC could be the first
string you pull from that ball of fun. You can start here. ↩
2. You maybe interested in a bit of historical context. When Microsoft first came out and

announced .NET and this concept of a common language runtime, quite a few people
(including me, sad to say) scoffed at the idea. Back then, there was no C#, or it was in
its infancy. Borland C++ and things like Delphi had a lot of mind share, if not market
share. It's been quite a journey and fascinating thing to look back and realize how far
we've come as an industry. ↩
3. Web browsers are not, strictly speaking, virtual machines. However, since we're

merely trying to be practical in this chapter, it's close enough. ↩


4. Internet explorer, in particular, has plagued me over the years. It plagues me today.


5. This handy web site helps identify how different browsers implement html markup,

CSS and JavaScript: https://caniuse.com/. ↩

20
Chapter 4: Putting the Type in TypeScript

Putting the Type in TypeScript


TypeScript's name is no accident. It doesn't mean "some type of scripting language."1
TypeScript overlays static typing on top of plain JavaScript. This is best explained by
example. Let's start with some valid and admittedly ridiculous pure JS code:

var xyzzy = "transport me!";


var TheAnswer = 42;
var hammerTime = new Date(1990, 1, 13);
var BookTitles = [];

The above snippet2 shows four variables and JavaScript infers their data type. This allows
us to write code like this:

xyzzy = "you've been transported";


TheAnswer = TheAnswer + 1;
hammerTime = hammerTime.addDays(5);
BookTitles = ["Title 1", "Title 2", "Title 3"];

Pure JavaScript also lets us do things like the following:

var xyzzy = "transport me!";


xyzzy = xyzzy + 20;

var TheAnswer = 42;


TheAnswer = true;

var hammerTime = new Date(1990, 1, 13);


hammerTime = {};
hammerTime = hammerTime + 5;

var BookTitles = ["Title 1", "Title 2", "Title 3", "Title 4"];
BookTitles[2] = [{Title: "SomeTitle", TotalPages: 200}]

console.debug("xyzzy:",xyzzy);
console.debug("TheAnswer:",TheAnswer);
console.debug("hammerTime:", hammerTime);
console.debug("BookTitles:", BookTitles)

That's valid script and in chrome, the output looks like this:

21
Chapter 4: Putting the Type in TypeScript

Figure: Nonsensical But Allowed Variable Assignments

A lot of people really don't care for this behavior3. As I say, this is a contrived example. If
you're prone to writing code like this, you may not belong in the field. The real problem is
that is very, very easy to introduce bugs in pure JS by accidentally mixing data types.
TypeScript mitigates the problem. In TypeScript, you can specify the type of the variable
when you define it. Let's look:

var xyzzy: string = "transport me!";


var TheAnswer: number = 42;
var hammerTime = new Date(1990, 1, 13);
var whoKnows;

The above snippet explicitly shows TypeScript's type system at work:

The variable xyzzy is a string .


"TheAnswer" is a number .
hammerTime is a Date . Why is it a Date and not something else? Because TypeScript
can infer its data type. The code initializes it to a Date object and hence, it can only be a
date.

22
Chapter 4: Putting the Type in TypeScript

The last variable, whoKnows , also has a type, any . Variables of type any act just like pure
JS variables. You can assign string values one moment, booleans the next and numbers
after that.

If you go to the trouble of defining types on your variables, your IDE will give you some great
edit-time and compile-time support. Consider this short animation:

Figure: IDE Supporting Defined Types

(If you can't see the animation for some reason, access this link via a web browser or type in
this URL: https://goo.gl/hEbWvq).

If you're already a JS coder, this is a very simple way to get started with the language. Pick a
few variables, associate some types with them and see what happens. A couple things will
or may happen when you do this:

Your IDE will get a lot smarter about your code. It will know variable types and prevent
you from assigning strings to numbers and that sort of thing.
You may discover problems with your code right away. You may well have intended that
a particular variable, myNumber , hold numbers. As JS coders know, it's quite easy to
mistakenly assign strings, date, complex objects, to your "myNumber" variable.

Many TypeScript developers start off this way because it's so simple to do. It's so simple, in
fact, that they quickly move on to more interesting typings, including the ability to strongly
type nested objects via interfaces . The next chapter introduces interfaces as data
descriptors.

23
Chapter 4: Putting the Type in TypeScript

Light Bulb Time?


In late December of 2015, Eric Clemmons posted a widely read article on Medium entitled
JavaScript Fatigue. You can read it here. If you haven't read it, it's probably worth your
time4. It does a good job of describing the ennui in which some JavaScript devs find
themselves trapped at times. It's a difficult trap to avoid! There are so many frameworks,
development tools, IDEs and other clever gidgety-gadgets, it can become ... fatiguing.

Many first-time TypeScripters shake off their skepticism and get a


little rush of excitement from this most simple of TypeScript features.
Merely add ": string" or ": number" to a variable and ...

1. It's much more difficult to mistakenly make mismatching


assignments.
2. Intent becomes clear. Any decent IDE will tell you, at runtime,
the data type of the variable.

This is very valuable stuff. It is also very easy to harvest some value from it. All you need to
do is:

1. Take one of your JS files


2. Copy it with a .ts extension
3. Add some typings (numbers, strings, booleans, etc.)
4. You're done!

All of the rest of your JS will work as normal. The most simple change to your code
immediately provides significant benefit. This was my light bulb moment. It didn't end there
for me and won't end there for you.

It's probably safe to say that if there's no light bulb going off for you right now, TypeScript
may not be for you, at least not today. I invite you to continue reading anyway :).

Declaring Variables
TypeScript provides three different ways to define a variable:

var keyword

let keyword

const keyword

If you declare a variable with the var keyword, it works exactly the same way it does in
pure JavaScript. It follows the same scoping rules and as such, you need to concern
yourself with unexpected hoisting effects and/or inadvertently polluting the global

24
Chapter 4: Putting the Type in TypeScript

namespace. const and let simplify things by reducing this risk and associated
complexity. Here's a bit of plain JavaScript code that implements a function,
getTempLabel() . It's mean to take in numeric temperature in Celsius and return a text label.

Here's the plain JS code:

function getTempLabel(currentTempInCelsius) {

if (currentTempInCelsius > 35 && currentTempInCelsius <= 40) {


result = "Very warm";
}

else if (currentTempInCelsius > 40) {


result = "Hot!";
}

else {
var result = "Unexpected temperature value.";
}

console.log(result);

return result;
}

Take note of three things from this example: 1) The variable "result" isn't actually decorated
with var until it makes its 3rd appearance. 2) Through the magic of "hoisting," result is
available throughout the function, not just in the else block where it's defined. 3) This is also
perfectly valid TypeScript, although there's a much better way to implement the function.
You'll see that in a moment.

Many programming languages dictate tighter scoping rules. Many people, the author
included, consider the above example poorly done for several reasons:

The variable isn't properly declared until well past its first use.
The code doesn't do a good job showing the developer's intent here. result could be
used anywhere in the function on both the left hand side and right hand side of an
expression, leading to unanticipated and difficult to track bugs.
Even experienced JS developers have a hard time with variable scope and hoisting.
Why accept that headache lying down?

Here is similar code written in TypeScript:

25
Chapter 4: Putting the Type in TypeScript

function getTempLabelTS(currentTempInCelsius: number): string {


let result: string;

if (currentTempInCelsius > 35 && currentTempInCelsius <= 40) {


result = "Very warm";
}
else if (currentTempInCelsius > 40) {
result = "Hot!";
}
else {
result = "Unexpected temperature value.";
}

console.log(result);

return result;
}

As you can see, instead of using var to define the result variable, the code uses
TypeScript's let . Let defines variable characteristics the same way as var - you specify a
name and optionally a data type. The difference is about variable scope. A variable defined
with let is scoped to the block where it's defined and is available to sub-blocks. It is never
hoisted, as happens in pure JavaScript. Watch this short to see the effect of let and variable
scope in a few different scenarios:

(Depending on how you're reading the book, the video may not appear. If not, access it by
clicking this link or typing the following URL into your web browser:
https://youtu.be/tMkqzIqhCwo)

TypeScript Best Practice - Let

26
Chapter 4: Putting the Type in TypeScript

As a rule, prefer "let" over var. This will tend to reduce the risk of unanticipated side effects
in your code through JavaScript's hoisting mechanism.

Const Definitions
TypeScript provides another method for defining variables - const . A const variable:

1. Must be initialized when declared.


2. May never be changed.

Here's an example:

const myName: string = "Paul"


myName = "Mary"; // <-- compiler error

const brings a little subtlety to the table, especially when it comes to object property

values. Consider this bit of code:

const Paul = { firstName: "Paul", lastName: "Galvin"}


const Kelly = { firstName: "TBD", lastName: "TBD"}
const Aidan; // <-- Not allowed, must always initialize const variables when defined
Kelly.firstName = "Kelly"; // <-- perfectly OK
Paul = null; // <-- Not allowed, cannot use const vars in LHS of an assignment

The code defines three any variables5 and you can tell that it's mean to hold a kind of
"person" record, holding a first and last name. It initializes Paul and Kelly to similarly
structured objects.

It then tries to create a constant Aidan variable without assigning an initial value. This is not
allowed. It won't compile and your IDE should highlight this as an error.

The Kelly PersonName const variable is defined. However, it's seeded with "TBD" values.
Later, the code changes Kelly's firstName property. This is valid6.

Lastly, the Paul variable cannot be changed after it's initialized. The final "Paul = null"
assignment is also invalid. Const variables may never be in the left hand side of an
assignment once they are declared and initialized.

TypeScript Best Practice - Let

27
Chapter 4: Putting the Type in TypeScript

As a rule, prefer const over let . This recommendation largely derives from functional
programming principles. The more you minimize mutations in your code, the fewer side
effects you'll experience7.

Taking this and let into consideration, we can summarize:

Prefer const in all cases.


When const won't meet your needs, prefer let over var.
If you absolutely need var, use that. However, the use of var in TypeScript strongly
indicates a logic problem and an opportunity to simply the code.

TypeScript Let, Const and Transpilation


At the end of the day, JavaScript doesn't know anything about const or let. They always
compile down to plain old var statements. Here's the transpiled version of the
getTemperature function from above:

function getTemperatureLabel(forTemperature) {
var errorResult = "ERROR: Failed to determine a temperature label!";
var result;
if (isNaN(forTemperature)) {
result = errorResult;
}
else {
var options = ["Cold", "Warm", "Hot"];
result = forTemperature < 40 ? options[0] :
forTemperature < 85 ? options[1] :
options[3];
}
return result;
}

TypeScript enforces variable scope and const initialization / assignment rules at compile-
time. A good IDE will do it as you write the code.

Further Reading
Consider reading the following articles that compliment this chapter's content:

A pretty good conversation on stackexchange that plumbs the depths of let vs var
vs const : https://softwareengineering.stackexchange.com/questions/278652/how-
much-should-i-be-using-let-vs-const-in-es6

28
Chapter 4: Putting the Type in TypeScript

A good overview of var/let/const as well as describing a few primitive data types:


http://www.brainbell.com/typescript/data-types-let-var-cons.html

Another good overview article by Ayo Alfonso contrasting JavaScript with TypeScript:
https://hackernoon.com/typescript-vs-javascript-b568bc4a4e58

Summary
In this chapter, you learned that TypeScript is a statically typed langauge that introduces a
couple of new ways to manage variable scope and immutability. You saw some of the
practical effects that derive from these features and have been armed with a bit of advice on
how to use them.

The next chapter digs into this topic with more gusto and introduces interfaces, a most
useful and powerful language element. It also covers enumerations, union types and more!
Take a deep breath and then turn the virtual page.

1. Although that would be truly glorious. ↩

2. Three of these variables remind me of my youth and for history's sake, here are

some links for you to follow if you don't know them already: 42, xyzzy (which you can
play on the Amazon Echo, believe it or not(!)) and Hammertime. ↩
3. To be fair, plenty of people are perfectly OK with it. For example, Jeff Walker asserts

that: ↩

TypeScript enhances JavaScript with types, classes and interfaces. Some people think
that is the problem with JavaScript. It’s not. The problem with JavaScript is not that it is
a dynamically typed prototype based object-oriented language without classes. That is
actually JavaScript’s strength."

I don't know JW and I don't mean to imply that this one quote pulled from one article he
wrote stands for everything he believes :). That quote does, however, do a good job
articulating a certain school of thought vis-à-vis JavaScript's dynamic nature. Many people
like it. Those people probably aren't using TypeScript much.

Eric Elliot takes a deeper dive into the subject: https://medium.com/javascript-scene/you-


might-not-need-typescript-or-static-types-aa7cb670a77b#.5aideomvb. This is also worth
reading.

29
Chapter 4: Putting the Type in TypeScript

4. It's also, a little ironically, a decent listing of interesting tools and frameworks out

there and hence, another good reason to read the article. That is, of course, it doesn't
tire you out. To be safe, finish reading this book first. ↩
5. Recall that the default data type is "any" for TypeScript variables. You should avoid

this, especially if you're starting off with a fresh new project. You can disallow "implicit"
any variables through a compiler configuration setting. Read about that here:
https://basarat.gitbooks.io/typescript/docs/options/noImplicitAny.html. ↩
6. Admittedly, this is a minor source of cognitive dissonance. Since the variable is itself

a const , why allow us to change the variable's properties as well? It is what it is and
turns out to be helpful at the end of the day. So, live with it we must. ↩
7. I have found that functional programming, like TypeScript, is a joy unto itself. I have

written about that joy here: https://hackernoon.com/unexpected-joy-from-functional-


programming-ed9d3adca77a. ↩

30
Chapter 5: Types in Depth

Complex Types Using Interfaces

What's Covered
We're going to start off this chapter by introducing TypeScript interfaces. This chapter
examines interfaces in the context of JavaScript "data" objects and their fields/properties. As
many of you know, interfaces play an over-sized role in many common design patterns (think
SOLID1). We will talk about interfaces in that context in Chapter 9.

TypeScript provides other more advanced typing support that you may have come across in
C# and Java. This chapter covers some of them, including:

Enumerations: Attach a human-friendly label to a


A Note About Generics
number. Generics offer a very powerful data typing
capability. They look and act a lot like
Unions: A variable can be a "number" or "string" or generics in C# and are a very effective tool
helping you adhere to the Don't Repeat
"MyBrandShinyNewObject" but not anything else.
Yourself (DRY) principle. If you aren't familiar
Custom types: Think classes but without a constructor. with DRY, here's one place you could start:
http://deviq.com/don-t-repeat-yourself/
(If you don't know about classes, don't worry, you'll Although generics are part of the type system,
the tend to go hand in hand with classes, so
learn a it about them in chapter 9). we'll hold off on describing them until chapter
10 after you've had a chance to read about
and digest TypeScript classes.

Interfaces as Data Describers


Declare a TypeScript interface like this:

interface myInterface {
}

That code defines a new interface called "myInterface". It's an empty interface, but
nonetheless valid2.

Variables can now declare their type as being that interface:

const myVariable: myInterface;

Although there are some use cases for empty interfaces, you'll normally use them this way
to describe complex objects. Let's consider a business scenario and implement a supporting
data structure in plain JavaScript.

31
Chapter 5: Types in Depth

Your client owns a book store and you're developing a simple app that lets your client's
customers view a listing of all available books. In JavaScript object terms, a "book" has
these properties:

Author
Title
Genre (e.g. biography, history, sci-fi)
Short Description
Total Pages
Condition (e.g. New, Great, OK, Not Great)

In pure JavaScript, we might model a book this way:

var bookModel = {
Author,
Title,
Genre,
ShortDescription,
TotalPages,
Condition
}

That's simple enough. We have an object called "bookModel." The developer's intent is
pretty clear, although there's actually plenty of room for improvement. If you want to re-use
3
bookModel in pure JavaScript, you could clone it :

var aBookInstance = (JSON.parse(JSON.stringify(bookModel)));

In TypeScript, we can use interfaces to define a better shape and even self-document the
model. Here is one way to do it:

interface BookModel {
Author: string;
Title: string;
Genre: string;
ShortDescription: string;
TotalPages: number;
Condition: string;
}

When we want an actual book instance, we define it like this in TypeScript:

let aBookInstance: BookModel;

32
Chapter 5: Types in Depth

This interface shows three immediate advantages TypeScript provides over JavaScript:

1. The developer's intent is much clearer. You can tell that TotalPages is meant to hold
4
numeric values while the rest are meant to hold strings .
2. Spot-on intellisense.
3. It's really a model. It's not a JavaScript variable masquerading as model. In fact, when
you compile a TypeScript interface, it produces no JavaScript at all. Only the compiler
knows about the interface. There is no run-time artifact.

Let's assume you agree that TypeScript conveys the the dev's intent more clearly than the
pure JS example5. Here's a short 40 second video showing VS Code intellisense at work:

(Depending on you're reading the book, that video may not appear. In that case, click this
link or go directly to the YouTube video with this link: https://youtu.be/o_wxodLGT34).

Here are some key takeaways from the video:

1. Once you define an interface, it becomes another candidate data type. Use it the same
way as the built-in data types, such as string, boolean, number, etc.
2. Once you define a variable with an interface data type, you must usually include all of
the interface fields. NOTE: As you'll soon see, it's possible to define optional interface
components as well.
3. It's not enough to add all of the interface fields to the "aBook" variable. You must also
add them with the correct type. In the video, I tried to assign a string value to

33
Chapter 5: Types in Depth

"TotalPages" field but the IDE told me that was not allowed.

Refactoring with Interfaces


Interfaces give us even more meaningful information and it's particularly useful when we
refactor our code.

Let's imagine that we need to change our book model. When we started, we didn't realize
that many books have multiple authors. As a result, we need to refactor the model and make
Author an array of strings, not just a scalar / single string.

In pure JS, we don't need to do anything special. We just start writing code like this:

var bookModel = {
Author, // NOTE! On [such and such a date], this was converted to an array
Title,
Genre,
ShortDescription,
TotalPages,
Condition
}

var aBookInstance = JSON.parse(JSON.stringify(bookModel));


//aBookInstance.Author = "Paul Galvin";
aBookInstance.Author = ["Paul Galvin"];

It's a very simple change to make, but it's quite difficult to find all the places where you need
to make the change. You mostly have to do a global search in your IDE to find instances of
"Author" and refactor where you find them.

Contrast this with TypeScript:

34
Chapter 5: Types in Depth

(Depending on how you're reading the book, you may not be able to see the video. In that
case, try clicking here or use the following URL in your favorite web browser:
https://youtu.be/fNtcCTeMAhQ)

When I changed Author from string to string[] , I invalidated every instance of every
book model in the code. I can't run a successful build until I fix it. I still have a potentially
tricky refactoring task on my hands - after all, I still need to fix every place in the code that
references Author. However, the compiler won't let me miss any of those changes. That is
powerful stuff.

Nested Objects and Interfaces


Although BookModel is technically a complex object, it's not very complex. Let's spice things
up and take another look at "Author." We've already refactored the model to account for
multiple authors. Authors are normal people, just like the rest of us, and in the United States
and elsewhere, they usually have both a first and last name. In addition, authors love
feedback. To this end, we want the author's preferred email for feedback.

This next bit of code shows the new AuthorModel and refactors BookModel to use it.

interface AuthorModel {
FirstName: string;
LastName: string;
PreferredEmail: string;
}

35
Chapter 5: Types in Depth

interface BookModel {
Authors: AuthorModel[];
Title: string;
Genre: string;
ShortDescription: string;
TotalPages: number;
Condition: string;
}

// Example 1: Create an author object first, then add it to the book instance
const FoodBookAuthor1: AuthorModel = {
FirstName: "Paul",
LastName: "Galvin",
PreferredEmail: "galvin.paul@gmail.com"
}

const FoodBookAuthor2: AuthorModel = {


FirstName: "Kelly",
LastName: "Smith",
PreferredEmail: "ksmith123@awesomefoods.com"
}

const foodBook: BookModel = {


Authors: [FoodBookAuthor1, FoodBookAuthor2],
Title: "Foods - The Right Food for the Right Meal",
Genre: "Life Hacks",
ShortDescription: "Eggs are not for dinner",
TotalPages: 158,
Condition: "Used - Good"
}

// Example 2: Create a book instance in one line.


const GotM: BookModel = {
Authors: [{
FirstName: "Steven",
LastName: "Erikson",
PreferredEmail: "ganoes.paran@malazanempire.gov"
}],
Title: "Gardens of the Moon",
Genre: "High Fantasy",
ShortDescription: "Empress tries to conquer city, fails, but wins something better"
,
TotalPages: 772,
Condition: "New"
}

As you can see, TypeScript supports nested objects quite nicely.

36
Chapter 5: Types in Depth

If you're using VSCode or Visual Studio, try copying in the above code. Hover your mouse
over the Authors field in either Example 1 or Example 2 and then press F12. This will bring
you to the definition of the object. This is very handy when trying to understand the
underlying definition of a given type/interface.

Interfaces - Mapping a REST Response


We'll wrap up the discussion on interfaces by reverse engineering a REST response. In this
scenario, I'm making a call out to a SharePoint REST endpoint asking for a "user"6. When I
make the call, I get back a lot of information, starting with the HTTP wrapper around what I
really want:

Figure: HTTP Wrapper

The HTTP wrapper consists of:

config (complex object)


data (complex object)
headers (complex object)
status (number)
statusText (string)

We can define an interface that matches that:

interface httpResponse {
config: any,
headers: any,
status: number,
statusText: string;
}

37
Chapter 5: Types in Depth

The above example is a bit lazy - it's not trying to model the data underlying config or
headers . I'm waving my hands in their general direction by using "any." I certainly could

model those objects but I'm going to focus on data instead. You'll notice that "data" is
missing from the interface. Lets link that in. But first we need to define an interface that
models the data portion of the REST response. To start, I need to know what the REST
response is giving me:

Figure: Data Portion of REST Response

This interface maps things nicely:

interface userProfileRestModel {
Attachments: boolean;
AuthorId: number;
BPBrands: string[];
BPDescription: string;
"odata.editLink": string;
// and other user profile fields
}

38
Chapter 5: Types in Depth

Note the odata.editLink field in the response - if your object's name has otherwise invalid
characters in it, you can still get and set its values when you reference it via its name this
way.

Now it's time to link them in. Here's the code:

interface userProfileResponse extends httpResponse {


data: {
value: userProfileRestModel[]
}
}

Notice the extends keyword. I'm defining a new interface, userProfileResponse by


extending the previously defined httpResponse interface. The new userProfileResponse
interface contains all the fields and structure of both.

Here's another 40 second video that shows this visually.

(Depending on how you're reading the book, you may not see the video. In that case, try
clicking here or go to YouTube directly in your web browser:
https://youtu.be/oK3MpqhrVOo.)

The last dozen seconds of the video show you that the IDE understands the structure of the
new userProfileResponse interface.

Summarizing Interfaces
TypeScript interfaces are a very useful feature of the language:

They are very good at demonstrating the developer's intent

39
Chapter 5: Types in Depth

IDEs understand their structure and provide great intellisense support.


They are better at modeling content than pure JavaScript.
If you need to refactor one of your models, it's much more difficult to miss something
since everywhere you use the interface breaks.

We're not finished with interfaces - they also play a role with classes. That's where a
significant amount of their pattern-implementation power comes from. Before we get to that,
we'll cover off several other great typing features - enums, unions and custom types.

Enumerations and Union Types


So far, we've covered primitive data types (numbers, boolean, etc.) and how you can model
complex objects using these primitive types. You can, in fact, create deeply nested data
models using interfaces themselves. TypeScript provides additional ways to describe data.
We'll look at two more of them: enumerations and unions. Note that TypeScript provides
even more types such as intersection types, generics and type aliases. Some of these (e.g.
intersections) cater to tools writers more than the casual audience I have in mind for this
book. Generics, on the other hand, deserve their own chapter and work best with classes
and methods.

Enumerations
Enumerations allow you to connect a string label to a numeric value. This is best shown via
example:

enum HttpStatusCodes {
OK = 200,
GENERAL_SERVER_ERROR = 500,
RESOURCE_NOT_FOUND = 304,
FORBIDDEN = 403
}

Use enumerations in your code like this:

40
Chapter 5: Types in Depth

function parseResult(resultDetails: SomeInterface, resultCode: HttpStatusCodes) {


if (resultCode === HttpStatusCodes.OK) {
processSuccessfulResponse(resultDetails);
}
else if (resultCode === HttpStatusCodes.FORBIDDEN) {
login();
}
else {
processOtherError(resultCode, resultDetails);
}
}

Many languages provide a similar enum syntax and if you've worked with one (like C# or
Java) this all looks very familiar.

As with everywhere else in TypeScript, a good IDE supports enumerations with intellisense.

The snippet above example shows that you can match a text label with an arbitrary integer
value. Sometimes, you don't care about the value. You just want the convenience of a
human-readable label to use in your code. In that case, you can define an initial value and
the compiler will increment it for you behind the scenes:

enum Direction {
Up = 1,
Down,
Left,
Right
}

In this case, Down, Left and Right are assigned the values 2, 3 and 4 respectively.

Mapping labels, such as "FORBIDDEN" to a number value "403" constitutes the main use
case for enums. Used this way, they allow you to express yourself more clearly in code. You
may be fully aware that an http 403 is a "forbidden" message but other, newer developers
may not.

Enums As Objects, Or Not


Unlike interfaces, the TypeScript compiler generates code for enums by default.

Here's the TypeScript Code and the generated JS:

41
Chapter 5: Types in Depth

enum HttpStatus {
OK = 200,
GENERAL_SERVER_ERROR = 500,
RESOURCE_NOT_FOUND = 304,
FORBIDDEN = 403
}

function parseResult(resultCode: HttpStatus) {


if (resultCode === HttpStatus.OK) {
console.log("Success response");
}
else if (resultCode === HttpStatus.FORBIDDEN) {
console.log("Forbidden response.");
}
else {
console.log("Some other response");
}
}

Here's the generated JavaScript:

var HttpStatus;
(function (HttpStatus) {
HttpStatus[HttpStatus["OK"] = 200] = "OK";
HttpStatus[HttpStatus["GENERAL_SERVER_ERROR"] = 500] = "GENERAL_SERVER_ERROR";
HttpStatus[HttpStatus["RESOURCE_NOT_FOUND"] = 304] = "RESOURCE_NOT_FOUND";
HttpStatus[HttpStatus["FORBIDDEN"] = 403] = "FORBIDDEN";
})(HttpStatus || (HttpStatus = {}));

function parseResult(resultCode) {
if (resultCode === HttpStatus.OK) {
console.log("Success response");
}
else if (resultCode === HttpStatus.FORBIDDEN) {
console.log("Forbidden response.");
}
else {
console.log("Some other response");
}
}

As you can see, TypeScript wraps the enum inside its own Immediately Invoked Function
Expression (IIFE) and lives on as a code artifact. Most of the time, this isn't useful. You can
skip the code generation and instead declare the enum as const :

Prefer Const Enums

42
Chapter 5: Types in Depth

You should normally prefer to use const


const enum constHttpStatus { enums. There are probably some good use
cases for non-const enums but you almost
OK = 200,
certainly won't encounter them in your first
GENERAL_SERVER_ERROR = 500, weeks and months with the language, if at all.
RESOURCE_NOT_FOUND = 304, Const enums generate less code and that
generated code is as easy to understand as
FORBIDDEN = 403
the non-const generated code.
}
This is also in keeping with the broader "use
function parseResult(resultCode: constHttpStatus) { const first" rule. If you can adopt that habit
you'll be taking some early steps toward a
if (resultCode === constHttpStatus.OK) {
more functional programming style and
console.log("Success response"); significantly reduce the risk of unanticipated
} side effects in your code.

else if (resultCode === constHttpStatus.FORBIDDEN)


{
console.log("Forbidden response.");
}
else {
console.log("Some other response");
}
}

This results in more compact JavaScript:

function parseResult(resultCode) {
if (resultCode === 200 /* OK */) {
console.log("Success response");
}
else if (resultCode === 403 /* FORBIDDEN */) {
console.log("Forbidden response.");
}
else {
console.log("Some other response");
}
}

It even puts in some helpful comments describing the the meaning of "403" or "200" if you
find yourself digging into the generated JS.

Union Types
Union Types allow you to create a define a new entity that is comprised of multiple types or
even values. Here's a simple example:

function move(inDirection: "left" | "up" | "down" | "right") {


console.log(`Moving ${inDirection}.`);
}

43
Chapter 5: Types in Depth

This bit of code defines a function, "move" that takes a single parameter, "inDirection."
Intellisense ensures that you don't try to pass in an invalid direction, like "sideways." Here's
a short video demonstrating that.

(If you can't see the video, try clicking here. Or, open your preferred web browser and go to
it directly: https://youtu.be/lfAa1-b-sng)).

This isn't a particularly great example since in cases like this, you would probably use an
enumeration instead or split it out into five functions (moveLeft, moveRight, moveUp,
moveDown and lower level "move" function). For a better use case, let's consider legacy
code. Let's say you have built a library of JavaScript utility functions and you want to start
using that library with a TypeScript project. Your library has a function,
calculateCollectionTotal . This function takes in an array of objects and as long as they

share a common field in common, "Total", it will add them all up and return the result. Here's
what that might look like:

function calculateCollectionTotal(itemCollection) {
return itemCollection.reduce(function(prev, current) {
return prev + current.Total;
}, 0);
}

console.log("Invoice lines total:", calculateCollectionTotal(invoices));


console.log("Order lines total:", calculateCollectionTotal(orders));
console.log("Pick lines total:", calculateCollectionTotal(PickingSlips));

If you're converting this legacy code to TypeScript, The "correct" approach here is to refactor
the code, starting with a look at your invoices, orders and picking slips objects. Find their
common elements, define an interface or possibly an abstract base class7. Restructure all

44
Chapter 5: Types in Depth

the objects and update the overall code base. However, that's a lot of work. Union types can
help you right away without the need for so much refactoring. Here's what it could look like:

function calculateCollectionTotal(itemCollection: Invoice[] | Order[] | PickingSlip[])


: number {
return itemCollection.reduce(function(prev: number, current: Invoice | Order | Pic
kingSlip) {
return prev + current.Total;
}, 0);
}

console.log("Invoice lines total:", calculateCollectionTotal(invoices));


console.log("Order lines total:", calculateCollectionTotal(orders));
console.log("Pick lines total:", calculateCollectionTotal(PickingSlips));

This bit of TypeScript does the same thing as its plain JS cousin. However, it adds in some
type safety that your IDE's intellisense feature can use. It's also nicely self-documenting.
With one look at the signature, it's plain to anyone that this function was designed to
calculate totals on a specific set of objects and no other objects.

You'll read about a better way to accomplish this using generics but they would force you to
make a bigger change to your code base.

Further Reading
The following articles provide alternative and/or a deeper dive into the topics discussed in
this chapter:

This article plus video covers pretty much the same ground as I do with interfaces
above, with but with a lot fewer words :). http://tech.queryhome.com/153271/what-is-
interface-in-typescript?utm_source=dlvr.it&utm_medium=twitter
A lengthy article that talks about using interfaces and unions to model data:
https://www.triplet.fi/blog/different-approaches-to-modeling-data-with-typescript/
TypeScript 2.4 (which came out just as I was finished version 1.0 of this book)
introduces string enums! You can read about them straight from Microsoft's blog:
https://blogs.msdn.microsoft.com/typescript/2017/06/12/announcing-typescript-2-4-rc/.
That blog is a very good source and you should keep an eye on it regularly.
TypeScript provides more sophisticated support for enums. You are not limited to
assigning integers and in fact, you can assign values that are computed at runtime. This
is best explained by the TypeScript language handbook web site here:
https://www.typescriptlang.org/docs/handbook/enums.html

45
Chapter 5: Types in Depth

Summary and Recap


This chapter introduced interfaces for the first time in their capacity as data describers.
Interfaces may be empty, they can describe a collection of primitive values (number, string,
etc.). You can create one interface and extend it to another. They can represent nested
objects, including deeply nested objects such as overly complex SharePoint JSON
payloads.

You also read about enumerations and union types. Both of these help make programmer
intent clear and help you avoid making mistakes in your code by ensuring that only certain
types of values or objects can be passed into functions.

We're going to pause from heady subjects and diverge into template strings next. It's a nice
and simple subject before we get into classes.

1. If you aren't familiar with this SOLID acronym, it's probably worth your time checking

it out. This scotch.io write-up is a good start (https://scotch.io/bar-talk/s-o-l-i-d-the-first-


five-principles-of-object-oriented-design). ↩
2. Empty interfaces aren't typically all that useful, but this article on binary searches in

TypeScript provides one: https://blog.hellojs.org/implement-binary-search-in-typescript-


using-generics-with-useful-refactorings-a4bcda932d7. This one may be a little on the
complex side given where we are in the book, but it's worth coming back to once you
finish. ↩
3. There are a ridiculous number of ways to clone JavaScript objects. The approach I

used in these examples comes from this clever blog post: http://heyjavascript.com/4-
creative-ways-to-clone-objects/ ↩
4. It's pretty obvious that a property named "TotalPages" would be numeric. However,

as this chapter progresses, you'll see how interface show developer intent when
describing a less obvious properties. ↩
5. If you don't agree, then I don't know what else I can tell you :). ↩

6. If you happen to know anything about SharePoint - I'm not retrieving an SPUser

here, I'm retrieving an item from a custom list. ↩


7. Abstract classes, along with interfaces, provide a solid basis for your SOLID

programming efforts. The book covers abstract classes in chapter 9. ↩

46
Chapter 5: Types in Depth

47
Chapter 6: Template Strings

Template Strings
Template strings only just barely warrant their own chapter. They aren't a kind of type nor are
they any one of the building blocks TypeScript provides to support object oriented
programming. However, they are quite useful. Most of the examples going forward in the
book use them.

A template string is a "regular" JavaScript string seeded with placeholders. At runtime, your
code replaces the placeholders with real values.

Denote a placeholder with ${} . Place the variable or function inside the curly braces and it
will be evaluated at runtime.

Here's an example:

const userName: string = "Paul";


const helloTemplate: string = `Hello, ${userName}!`;
console.log(helloTemplate);

At runtime, the code replaces userName with the actual value of userName.

Here's another example that uses a template string as a parameter to console.log() .

function getRandomName() {
const names: string[] = ["Paul","Aidan","Kelly", "Amina"];
return names[Math.floor((Math.random() * 4));
}
console.log(`Hello, ${getRandomName()}`!);

In the above example, the placeholder is a function.

This is a nice shortcut, especially when you contrast it against the plain JS equivalent:

function getRandomName(): string {


var names: string[] = ["Paul","Aidan","Kelly", "Amina"];
return names[Math.floor((Math.random() * 4));
}
console.log("Hello, " + getRandomName() + "!");

You can use as many placeholders as you like:

48
Chapter 6: Template Strings

const name: string = "Paul";


const graduatedYear: number = 1987;

console.log(`${name} graduated in ${graduatedYear}.`);

I have trained myself to use the backtick character whenever I need to define strings. You
never know when you might want to introduce some data into a string at run-time.

You'll see a video showcasing template strings in the next chapter.

Template strings actually support a bit more functionality called "Tagged Templates." These
allow you to pre-process the string before it's emitted. I've never seen that functionality used
in the real world so I'm not covering it here. Read about there here and decide for yourself
whether you think you'll use them: https://basarat.gitbooks.io/typescript/docs/template-
strings.html

That's it for string templates. Let's get back into the weeds and dive into TypeScript
functions.

49
Chapter 7: Functions

Functions
TypeScript supports JavaScript functions as you already know them. As it often does,
TypeScript adds new functionality, including:

Function Types
Typed Parameters
Optional Parameters
Rest Parameters1
Arrow Functions (often also called Lambda or Anonymous functions)

Function Types
Everything has a type in TypeScript. If you don't specify a type, TypeScript infers a type and
for functions, it's Function . You can declare a variable's type to be Function as shown:

let sillyAdderFunction: Function;

sillyAdderFunction = function(a, b) { return a + b};

console.log(sillyAdderFunction(10, 10);

This isn't a terribly useful thing to do but it shows that there is a Function data type. You'll
see down below that Arrow Functions are far more useful.

Function Parameters
Most functions take at least one parameter and most useful functions return a value2. You
can specify a type for each function parameter as well as the return type of the function
itself. Here's a more robust integerAdder() :

50
Chapter 7: Functions

function integerAdder(firstNumber: number, secondNumber: number): number {


return firstNumber + secondNumber;
}

const TwoPlusTwo = integerAdder(2, 2);

// Error: can't pass string to numeric function argument.


const errorAdder1 = integerAdder("ham", "cheese");

// Error: errorAdder2 is a string but the function returns an integer.


const errorAdder2: string = integerAdder(2, 2);

The code defines a function, integerAdder . It takes two parameters and as you can see,
they are both integer values. Furthermore, the integerAdder function itself returns a number.

Here's a 30 second video showing this in practice:

(If you can't see the video, try accessing it via this link or type this url into your web browser:
https://youtu.be/zUI2Ygfdhj0).

Note that integerAdder specifies both the types of its arguments and the type of its return
value: integerAdder(firstNumber: number, secondNumber: number): number . This tells the
TypeScript compiler enough information that it prevents you from making two mistakes
common in the plain JavaScript world:

You can't pass in non-numeric values to the integerAdder function.


The result is numeric. You can't invoke the function and accept its result into a non-
numeric variable.

Functions don't need to return a value. If you want to be explicit (and you probably do!),
specify a return type of void :

51
Chapter 7: Functions

function noOperation(): void {


return;
}

Optional Parameters
We can define functions that take optional parameters. Imagine that you have a function,
InitializeDataSet in your application. Sometimes, you want to initialize the data set to a

collection of hard coded values (i.e. defaults). In other cases, you want to provide some
"seed" data.

function InitializeDataSet(seedData?: any) {


if (seedData) {
// use the seed data to initialize the data set
}
else {
// initialize using some default hard coded values
}
}

Use the question mark (?) to denote an optional parameter.

At run-time, client code invokes the function as normal. If that code does not supply a value
for the optional parameter, its value is undefined .

Default Parameter Values


You can specify a default value for your function's parameters. Here's the previous example
re-written to show this syntax and discuss the implication of using it:

52
Chapter 7: Functions

function InitializeDataSetWithDefaultValues(seedData = { seedValue1: "a", seedValue2:


"b"}) {
// seedData will have valid data so no need to check it.

/*
if (seedData) {
// use the seed data to initialize the data set
}
else {
// initialize using some hard coded values
}
*/
}

InitializeDataSetWithDefaultValues();
InitializeDataSetWithDefaultValues(undefined);
InitializeDataSetWithDefaultValues({seedValue1: "x", seedValue2: "y"});

Note that:

The function takes an input parameter, seedData . TypeScript infers the parameter's
type as an object with two string properties.
If client code does not pass a value for seedData, it will use the specific initial value.
If you pass the value undefined to the function, it will also the specified initial value.

Rest Parameters
Sometimes you want to pass an unknown number of parameters to a function. This case
frequently arises during React development, serializing and deserializing data and mapping.

Let's consider a logging example. We can always use console.log() to log out messages
to the console. However, it's a real challenge debugging applications at run-time, particularly
errors reported by end users. By the time anyone tells you about the error, it's too late for
you to do much about it. Let's add some persistent error logging to our toolkit:

53
Chapter 7: Functions

function myLogger(msgType: "INFO" | "ERROR", ...messages: any[]) {


if (msgType === "INFO") {
console.log(messages);
}
else {
// Save the details to local storage for future analysis/debugging
localStorage.setItem("lastErrorMessage", JSON.stringify(messages));
console.error(messages);
}
}

myLogger("INFO","Greetings!");
myLogger("INFO", "Successfully saved the data, results:", {someResult: "", databaseRes
ultCode: 1});
myLogger("ERROR", "ERROR: Failed to save the data, error details follow.", {errorDetai
ls: "[some error details object goes here]"}, "Error occurred at `${new Date()}`");

The code does the following:

Defines a function, myLogger .


myLogger takes two parameters: msgType and messages .
msgType is a union data type - client code must pass "INFO" or "ERROR".
messages is an array of any . Note that ... preceding the variable name. This
indicates that all of the remaining arguments will be stuffed into the array.
You can see how the code invokes the myLogger function, passing in a variable number
of arguments across its three invocations.

Arrow Functions
Arrow functions take their name from their syntax.

Many TypeScript developers prefer to call these lambda functions and sometimes
anonymous functions. They almost always mean the same thing.

Arrow functions are tremendously useful. They provide a nice syntax shortener and more
importantly, help reduce confusion over JavaScript scopes by redefining the this keyword
into something more intuitive.

Here's a very simple example to get us started on the syntax:

const myHelloFunction = () => { return "Hello!"};

myHelloFunction();

54
Chapter 7: Functions

This example defines and then invokes a function named myHelloFunction . Since
myHelloFunction is const we need initialize it at definition-time. That's this bit: {return
"Hello!"}; myHelloFunction is now a normal function and we can invoke it like any other

JavaScript function using the function invocation operator, () : myHelloFunction() ;

We don't need to define the function body. For instance, this is perfectly good syntax:

let myGoodbyeFunction: () => string;


myGoodbyeFunction = () => {return "Good Bye!"}
console.log(myGoodbyeFunction());

The first line defines a variable, myGoodbyeFunction . myGoodbyeFunction's type is a function


that takes no parameters and returns a string.

The second line assigns a value to myGoodbyeFunction. In this case, it's the function body
itself.

Lastly, the code logs out the result of executing myGoodbyeFunction.

Before we go any further, lets look at the transpiled JavaScript. Here's the myHelloFunction's
transpiled JavaScript:

var myGoodbyeFunction;
myGoodbyeFunction = function () { return "Good Bye!"; };
console.log(myGoodbyeFunction());

Here's the line-by-line transformation:

TypeScript Transpiled JavaScript

1. let myGoodbyeFunction: () => string; var myGoodbyeFunction;

2. myGoodbyeFunction = () => {return "Good Bye!"} myGoodbyeFunction = function () { return "Good Bye!"; };

3. console.log(myGoodbyeFunction()); console.log(myGoodbyeFunction());

let transpiles into var . The arrow function transpiles into a straight-forward function

definition.

Specifying Parameters in Arrow Functions


We specify parameters by inserting them info the () portion of the function definition.
Here's an adder function that takes two integer inputs and returns a numeric result:

55
Chapter 7: Functions

let myAdderArrowFunction: (arg1: number, arg2: number) => number;


myAdderArrowFunction = (firstNumber: number, secondNumber: number) => {
return firstNumber + secondNumber;
}
console.log(myAdderArrowFunction(2, 2));

The code defines a new function, myAdderArrowFunction , using the arrow syntax.
myAdderFunction takes two numeric arguments and returns number.

It's important to keep in mind that the let statement is merely defining a typed variable
named myAdderFunction. It type happens to be a Function with typed signature and a
typed result.

The second line initializes myAdderArrowFunction. Note that I didn't use the same names
when specifying the input parameters. Again, the let statement is defining a type - a
Function who takes in two arguments and returns a number. As long as meet the
requirements of the type, the parameter names don't matter.

Arrow function as interface components


Given that arrow functions can define types independent of their implementation, you can
use them anywhere else you would use a type, including interfaces. This is a very useful
capability for many reasons. One reason comes into play when you're working with an
external library that was not written in TypeScript. This obviously happens a lot in the real
world3. Let's assume that we are working with one such library that is responsible to create
detailed, highly interactive visualizations. We don't know how it works and we don't care. We
consult the library's documentation and see that it provides a robust JavaScript API that
includes the following functions:

Render : We provide the ID of a <div> tag on our page and it renders the visualization

there.
SetDimensions : We can set the height an width with this call.

SaveSettings : We ask the engine to save the current screen settings for use next time.

Here's some TypeScript that lets inject some strong typing into our code despite the fact that
the vendor's API does not:

56
Chapter 7: Functions

interface IVisualizationEngine {
Render: (htmlDivName: string) => boolean;
SetDimensions: (width: number, height: number) => void;
SaveSettings: () => boolean;
}

// Assume that the visualization engine was already loaded


// and that we can get a handle to its API set via the global window object.
const myVisualizationEngine: IVisualizationEngine = <IVisualizationEngine>window["Visu
alizationEngine"];

if (myVisualizationEngine.Render("myDiv")) {
myVisualizationEngine.SetDimensions(1024, 800);
if (myVisualizationEngine.SaveSettings()) {
console.log("Successfully saved the visualization.");
}
else {
console.error("ERROR: Failed to save the visualization.");
}
}
else {
console.error("Failed to render the visualization!");
}

This is complex example. Let's unpack it a bit:

The code defines an interface, IVisualizationEngine .


The interface defines three different functions, one for each of the API calls we care
about4.
We get a handle to the engine via the global window object. In order for this to work, we
had to reference the engine via a script tag in our HTML and the engine would have to
save itself in the global window.

At this point, we've done something really nice for ourselves. We now have strongly typed
access to this third party's API! This allows the IDE to give us the great time-saving and
error-reducing intellisense we've all grown to love so much.

Here's another video showing these concepts. In this case, we'll model an existing contacts
management library that has been in production and stable for a long time. The video
creates an interface to that plain JS library and then uses it:

57
Chapter 7: Functions

(If you can't see the video, try clicking this link to see it directly on YouTube. You can also try
typing this link into your favorite web browser: https://youtu.be/e1BGcBO8E6U.)

Arrow Functions as IIFEs


As we saw above, arrow functions transpile down to standard JavaScript functions. We can
invoke functions as they are defined. These are called Immediately Invoked Function
Expressions (IIFEs). Arrow functions can also be invoked at any time. Here's a contrived
example:

console.log(`Hello, ${(() => {return "Paul";})()}`);

This code defines a function here: () => { return "Paul"} . It encloses that line in its own
set of parenthesis and then uses the invoke-function operator, () to immediately invoke
the function. This is itself wrapped inside a template string and finally, the result is logged out
to the console.

Here's the transpiled JavaScript:

console.log("Hello, " + (function () { return "Paul"; })());

This (possibly over-produced :)) video shows it live:

58
Chapter 7: Functions

(If you can't see the video, try clicking here or typing this URL into your web browser:
https://youtu.be/DTBpHxWPf_w.)

Arrow Functions for Cleaner Code


The previous example doesn't show it well, but arrow functions can help you do more than
teach your IDE about function types. It can also help you write cleaner code, although this
may admittedly be in the eye of the beholder. Here's an example:

const numbers: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];


const evenNumbers = numbers.filter( (item) => !(item % 2));
const sumOfNumbers = numbers.reduce((prev: number, curr: number) => {
return prev + curr;
}, 0);

In this example, we define an array of numbers, one through ten.

We then define a variable, evenNumbers . evenNumbers is the result of filtering on the


numbers array, passing back only those items where mod two equals zero. This is a new
form for us:

(item) => !(item % 2)

We could have written this fully out to:


(item) => { return !(item % 2); }

It's a bit of shorthand that we're allowed do when you can return a value with a single
statement.

59
Chapter 7: Functions

The second example adds up all the numbers using the standard reduce function. Reduce
runs against an array and takes a function and initial value as an argument. That function is
passed the previous value and the current value, defined in the example as (prev: number,
5
curr: number) . Note that reduce also passes a 3rd argument, the index of the item in the

array. We don't care about that so we don't bother catching it, so to speak.

Most TypeScript developers come to find Arrow functions to be very helpful and generally
cleaner looking than spelling out the word "function" all the time.

Further Reading
This blog post describes most of the same things covered here, along with two narrated
videos: http://executeautomation.com/blog/working-with-functions-anonymous-and-
arrow-functions-in-typescript/

This blog post provides a nicely detailed view into Arrow Functions and in particular, it
explains how this works in that context. http://piotrwalat.net/arrow-function-
expressions-in-typescript/

Summarizing Functions
TypeScript enhances plain JavaScript functions by...

Defining the types of function parameters (string, boolean, even interface types)
Defining the return type of a function (not just its parameters)
Specifying default values for parameters
Specifying a variable number of parameters via the rest operator ("...")

Like other Typescript artifacts, functions themselves are typed and their type is Function .

TypeScript introduces a different kind of function, the Arrow Function. We haven't heard the
last of arrow functions. We'll revisit them in chapter 9, "Classes in Depth" and explore how
they help simplify JavaScript closures. In short, they work in a more intuitive way with the
this keyword.

That concludes functions. The next chapter gently introduces basic TypeScript classes and
lays down the foundation for more advanced topics like abstract classes and how interfaces
work with them.

60
Chapter 7: Functions

1. Not to be confused with Representational State Transfer. Rest parameters are an

entirely different thing, as you'll see. ↩


2. If your function doesn't return any value, there's a good chance it's affecting some

data outside of its scope. This is usually a Bad Thing because it introduces the risk of
side effects. If you want more convincing, you could start here. ↩
3. This happens so much in the real world that TypeScript provides a significant feature

set to handle the challenge through the use of "typings files". The first cut of this book
doesn't address typings in detail but the "Continue Learning" chapter will point you in
the right direction. ↩
4. In this context, "care about" means that the library might provide other useful

functions but we don't plan to use them for one reason or another. We don't need to
map each of them to our interface definition, just the ones we plan to use. ↩
5. Reduce , along with filter and map tend to find themselves in code that adheres

to a functional programming style. I've written a small series of blog posts on this
subject up on my blog, https://hackernoon.com/functional-programming-the-examples-
series-851421e7ae5b. ↩

61
Chapter 8: Introducing Classes

Introducing Classes
TypeScript provides support for classes. Classes serve as a foundational component in
many object oriented languages. Loosely defined, a class is a collection of data and
functions that (usually) operate on that data. The data may or may not be accessible outside
of the class where its defined. Similarly, class functions may or many not be available
outside of their containing class. You get to make these decisions.

You can think of a classes as defining a template of functionality. That's the "data and
functions" part. At runtime, we create instances of classes and we normally call them
"objects." We often think in terms of "passing messages" or "invoking functions" on objects1.

Developers use classes to model people, places, business entities and concepts - all kinds
of things. Here's a simple example that begins to model a bus that might be used for public
transportation:

class Bus {
private myRouteNumber: number;
constructor(routeNumber: number) {
this.myRouteNumber = routeNumber;
}
public SayRoute() {
console.log(`My route is ${this.myRouteNumber}`);
}
}

Use the keyword class followed by the name of the class.

Public transportation authorities typically assign route numbers to busses. The Bus class
models the route number in a private field called myRouteNumber .

The constructor is a function that runs when client code instantiates an instance of the Bus
object. As you can see, constructors can take parameters and in this instance, the
constructor initializes the Bus' route number.

Our business rules dictate that busses must know how to "say" their name. A function,
SayRoute , meets the requirement by listing the bus's route number out to the console.

TypeScript introduces a bit of new lingo to describe classes2:

We generically call myRouteNumber, the constructor and SayRoute class members.


myRouteNumber is a property.
constructor is a special function that runs every time code creates a new instance of

62
Chapter 8: Introducing Classes

Bus. It runs only once per object instantiation but every time you create a new Bus
object.
SayRoute is a method.

Classes do nothing by themselves. They are much like cookie cutter templates - you can tell
what the cookie is going to look like but you have no cookie until you have cookie dough. We
create new objects as shown:

const myBeloved148 = new Bus(148);


const theDreaded164 = new Bus(164);

myBeloved148.SayRoute();
theDreaded164.SayRoute();

The above code declares two instances of the Bus object, "myBeloved148" (a super
express) and "theDreaded164" (a super local). It then invokes the SayRoute method on
each instance.

When we create a new object using the new keyword, we're instantiating the object. Some
people like to say "newing it up" instead. Here's a video showing the basics:

Protecting Your Class Data and Methods


You no doubt noticed a complimentary pair of descriptors, private and public . The Bus
class declares a private member, myRouteNumber. Private members (i.e. properties and
methods) may only be referenced or invoked within the object itself. Public members and
methods may be referenced both within the class itself, but also by client code. This means
that the following code will not compile:

class Bus {
private myRouteNumber: number;
constructor(routeNumber: number) {
this.myRouteNumber = routeNumber;
}
public SayRoute() {
console.log(`My route is ${this.myRouteNumber}`);
}
}
const myBus = new Bus(999);
myBus.myRouteNumber = 1234; // Throws an error, "Property 'myRouteNumber' is private a
nd only accessible within the class 'Bus'"

63
Chapter 8: Introducing Classes

As with every other part of the language, good TypeScript IDEs provide intellisense to help
you locate and use public and private methods properly. Here's a short video demonstrating
that point:

(If you can't see the video, try accessing it here or type this URL into your web browser:
https://youtu.be/QOvFFz1lRJM).

TypeScript classes go much deeper than this. The next chapter takes that dive.

1. These phrases, "passing messages" or "invoking functions" pretty much mean the

same thing. It can be helpful at times to view objects as living, breathing entities. This
paradigm lends itself to the "passing messages" concept. ↩
2. It's probably more accurate to say that it borrows the lingo from other languages. ↩

64
Chapter 9: Classes in Depth

Classes In Depth
This chapter covers TypeScript classes in more depth, including:

More on public and private methods and properties


Accessors (Getters and Setters)
Using interfaces to simplify constructors
Inheritance: Build a hierarchy of classes
Hiding Information: protecting your class data and methods from the outside world
Working with interfaces: Hide your implementation and enable greater levels of
abstraction
Abstract classes: Define templates with minimum standards of functionality

As you'll see, TypeScript supports classes in mach the same way as C#, Java and other
class oriented languages.

Public, Private and Generated JavaScript


The bus example from the previous chapter shows both public and private class members
( SayRoute and myRouteNumber respectively). You can declare both methods and properties
as public or private. Here's a slightly more complex example showing a private method and
public property:

65
Chapter 9: Classes in Depth

class Bus {

private myRouteNumber: number;


public SeatingCapacity: number;

private myRunCost: number;

constructor(routeNumber: number) {
this.myRouteNumber = routeNumber;

// Next line is allowed - objects may invoke their own private functions
this.myRunCost = this.calculateRunCost(30, 1.95);
}

public SayRoute() {
console.log(`My route is ${this.myRouteNumber}`);
}

private calculateRunCost(forDistance: number, fuelCostPerMile: number): number {


// Logic goes here to calculate cost for this bus to run this route.
// This function is invisible to client objects.
}

const myBus = new Bus(999);


myBus.SeatingCapacity = 80;

// Edit-time error since "calculateRunCost" is private


console.log(myBus.calculateRunCost(30, 1.95));

The code defines a new public property, SeatingCapacity . Since it's public, client functions
may both read (get) it and write (set) it. Client functions may not invoke the private method,
calculateRunCost . However, the constructor is allowed to invoke calculateRunCost since

they both belong to the same object.

Transpiled Objects and Implications Thereof


It's important to remember that TypeScript eventually compiles down to JavaScript. Let's
correct the TypeScript syntax error and show the resulting JavaScript.

TypeScript Bus Object:

66
Chapter 9: Classes in Depth

class Bus3 {

private myRouteNumber: number;


public SeatingCapacity: number;

private myRunCost: number;

constructor(routeNumber: number) {
this.myRouteNumber = routeNumber;
this.myRunCost = this.calculateRunCost(30, 1.95); // Allowed - objects may inv
oke their own private functions
}

public SayRoute() {
console.log(`My route is ${this.myRouteNumber}`);
}

private calculateRunCost(forDistance: number, fuelCostPerMile: number): number {


// Logic goes here to calculate cost for this bus to run this route.
// This function is invisible to client objects.
return 0; // Method signature requires us to return some numeric value to avoi
d syntax error.
}

const myBus3: Bus3 = new Bus3(999);


myBus3.SeatingCapacity = 80;
myBus3.SayRoute();
myBus3["myRunCost"] = 999; // Use bracket access to change the value of the "private"
class property, myRunCost

Transpiled JavaScript Bus Object:

67
Chapter 9: Classes in Depth

var Bus3 = (function () {


function Bus3(routeNumber) {
this.myRouteNumber = routeNumber;
this.myRunCost = this.calculateRunCost(30, 1.95); // Allowed - objects may inv
oke their own private functions
}
Bus3.prototype.SayRoute = function () {
console.log("My route is " + this.myRouteNumber);
};
Bus3.prototype.calculateRunCost = function (forDistance, fuelCostPerMile) {
// Logic goes here to calculate cost for this bus to run this route.
// This function is invisible to client objects.
return 0; // Method signature requires us to return some numeric value to avoi
d syntax error.
};
return Bus3;
}());
var myBus3 = new Bus3(999);
myBus3.SeatingCapacity = 80;
myBus3.SayRoute();
myBus3["myRunCost"] = 999;

As you can see, TypeScript creates an Immediately Invoked Function Expression (IIFE)
called Bus3 with the following characteristics:

Comments from TypeScript source emitted into transpiled JavaScript


A function named "Bus3". This maps to the TypeScript constructor.
Two prototype methods, "SayRoute" and "calculateRunCost".

In addition, it also shows that TypeScript can't always enforce privacy the same way that C#
and Java can. At the end of the day, you're working with JavaScript and you do anything that
JavaScript lets you do. This means access object properties using bracket notation.

Accessors (Getters and Setters)


TypeScript provides syntax to create Accessors, often called "getters" and "setters". These
are functions that look and feel a lot like properties but are, in fact, functions. Client code
uses them just like it uses any other property, Here's a very simple example:

68
Chapter 9: Classes in Depth

class Bus4 {

private _mySeatingCapacity: number;

public set SeatingCapacity(val: number) { this._mySeatingCapacity = val;}


public get SeatingCapacity() { return this._mySeatingCapacity;}

constructor() {
}
}

const theBus: Bus4 = new Bus4();


theBus.SeatingCapacity = 80;

console.log("Seating capacity:", theBus.SeatingCapacity);

The Bus4 class defines a private property, _mySeatingCapacity . It defines a corresponding


getter and setter, SeatingCapacity . Client code then interacts with SeatingCapacity as if it
were a public property by setting the value to 80 and then reading the value when logging it
out to the console.

TypeScript compiles getters and setters down to plain JavaScript "Object.DefineProperty"


calls:

var Bus4 = (function () {


function Bus4() {
}
Object.defineProperty(Bus4.prototype, "SeatingCapacity", {
get: function () { return this._mySeatingCapacity; },
set: function (val) { this._mySeatingCapacity = val; },
enumerable: true,
configurable: true
});
return Bus4;
}());
var theBus = new Bus4();
theBus.SeatingCapacity = 80;
console.log("Seating capacity:", theBus.SeatingCapacity);

If all you want to do is wrap a public getter and setter around a private property, it's hardly
worth the trouble1 - and indeed can be misleading. Here's a more comprehensive example
showing how a getter can perform a more meaningful calculation.

69
Chapter 9: Classes in Depth

class Bus5 {

private _myTotalPassengers: number;


private _myCostPerMile: number;
private _myTotalRouteDistance: number;

private _myRouteNumber: number;


public get myRouteNumber() { return this.myRouteNumber; }

private _mySeatingCapacity: number;


public set SeatingCapacity(val: number) { this._mySeatingCapacity = val; }

public get CostPerRider() {


const totalRouteCost = this._myTotalRouteDistance * this._myCostPerMile;
const costPerRider = totalRouteCost / this._myTotalPassengers;
return costPerRider;
}

constructor(routeNumber, costPerMile, totalPassengers, routeDistance) {


this._myRouteNumber = routeNumber;
this._myCostPerMile = costPerMile;
this._myTotalPassengers = totalPassengers;
this._myTotalRouteDistance = routeDistance;
}
}

const myBus5: Bus5 = new Bus5(148, 12.50, 72, 80);


myBus5.SeatingCapacity = 80;

console.log("My total cost per rider:", myBus5.CostPerRider)


console.log("Cost per rider with 80 riders: ", new Bus5(148, 12.50, 80, 50).CostPerRid
er)

This examples shows a getter, CostPerRider . When client code refers to the CostPerRider,
it calculates a value at run-time by taking into account distance, cost per mile and total
riders. It then returns that value.

A Note Regarding Accessors and


Performance
Be wary of long-running accessors. Some
frameworks, such as Angular 1.x, use two-
way binding to keep the UI in sync with the
back end data. If you bind a field in your
Angular template to a long-running accessor,
your users won't be wishing you a happy
birthday.

70
Chapter 9: Classes in Depth

Use Interfaces to Define Constructor


Arguments
In the previous example, the Bus5 object's constructor takes four numeric arguments as
input:

const myBus5: Bus5 = new Bus5(148, 12.50, 72, 80);

It's difficult to know what those arguments mean. Sure, intellisense helps a lot, but you need
to hover your mouse over the code to get that context.

We could have create an empty constructor and gone with public accessors or properties
instead. In this case, we'd end up with code like this:

const myBus5: Bus5 = new Bus5();


myBus5.RouteNumber = 148;
myBus5.CostPerMile = 12.50;
myBus5.TotalPassengers = 72;
myBus5.RouteDistance = 55;

There are at least two problems with this approach:

1. It requires public properties. This means that they can be changed after they've been
initialized and that can lead to pernicious side effects.
2. If you add a new public property, IDEs can't easily tell you everywhere you need to
change the code to initialize it.

Consider the second point. Let's say you have a non-trivial Bus management solution and
you're instantiating Bus objects in multiple modules in the solution. One day, you realize you
need to model a new property, StandardRouteTime to record how long a particular bus route
should take from start to finish. It's easy to update the class definition and and likewise easy
enough to update any given bit of code that creates a new instance of the bus object.
However, it can be hard to find every place that you need to change. Interfaces help solve
this, as shown in the following bit of code:

71
Chapter 9: Classes in Depth

interface Bus6Args {
routeNumber: number;
routeDistance: number;
costPerMile: number;
totalPassengers: number;
}

class Bus6 {
private _myTotalPassengers: number;
private _myCostPerMile: number;
private _myTotalRouteDistance: number;

private _myRouteNumber: number;


public get myRouteNumber() { return this.myRouteNumber; }

private _mySeatingCapacity: number;


public set SeatingCapacity(val: number) { this._mySeatingCapacity = val; }

public get CostPerRider() {


const totalRouteCost = this._myTotalRouteDistance * this._myCostPerMile;
const costPerRider = totalRouteCost / this._myTotalPassengers;
return costPerRider;
}

constructor(args: Bus6Args) {
this._myRouteNumber = args.routeNumber;
this._myCostPerMile = args.costPerMile;
this._myTotalPassengers = args.totalPassengers;
this._myTotalRouteDistance = args.routeDistance;
}
}

const myBus6: Bus6 = new Bus6(


{routeDistance: 44,
costPerMile: 12.50,
routeNumber: 148,
totalPassengers: 72});

myBus6.SeatingCapacity = 80;

console.log("My total cost per rider:", myBus6.CostPerRider)


console.log("Cost per rider with 80 riders: ",
new Bus6({routeDistance: 44, routeNumber: 148, costPerMile: 12.50, totalPassengers
: 80})
.CostPerRider)

This code is better for at least three important reasons:

1. Clarity
2. Long-term maintenance
3. Better information hiding

72
Chapter 9: Classes in Depth

Clarity
The code defines an interface, Bus6Args . The class constructor then takes an argument of
type Bus6Args. This allows us to write a line of code like this:

const myBus6: Bus6 = new Bus6(


{routeDistance: 44,
costPerMile: 12.50,
routeNumber: 148,
totalPassengers: 72});

This is a lot easier to understand than:

var myBus6 = new Bus6(44, 12.5, 148, 72);

It's immediately obvious what each of these four parameters do.

Long-term Maintenance
Recall the scenario from above - complex Bus management system with many modules,
thousands or more lines of code and many, many times when the code instantiates a new
Bus6 object. To model a new property, follow these simple steps:

1. Update the class definition to include the new property


2. Update the constructor and class business logic to make use of the property
3. Compile all the code.

The first time you do this, the TypeScript compiler will report an error everywhere you've
instantiated a new Bus object since all of your constructor arguments will be missing the new
property. This gives you a comprehensive checklist of every place you need to account for
this new property2.

Classes and Interfaces


Many common software design patterns find their best implementation rooted in interfaces.
In object oriented languages like TypeScript, C# and Java, developers use interfaces to
abstract implementation details and to create generic functionality that works against a
collection of seemingly disparate classes instead of individual named classes.

Classes, Interfaces and Data

73
Chapter 9: Classes in Depth

So far, we've used interfaces to define the "shape" of data. We can also use interfaces to
define the shape - the required properties - of a class. Let's step away from Buses for the
moment and think instead about a product recommendation engine. Imagine that you have a
database of clothing products such as pants, shirts, jackets, shoes, sneakers, etc. You've
created a nice search screen that allows users to state a preferred color and price range.
You want to iterate over all of your products and show anything that meets the user's
preferences.

We can easily model these products as classes and if we're careful about it, we can make
sure that each class includes a color and price property. This would then allow us to
iterate over a collection of these objects and recommend them based on the user's
preferences. Taking this approach, we might come up with a models like these:

class Shirt {
public color: string;
public fabricType: string;
public price: number;
public cut: string;
constructor() { }
}

class Shoe {
public color: string;
public size: string;
public price: number;
constructor() {}
}

class Pants {
public color: string;
public inseam: number;
public waist: number;
public price: number;
constructor();
}

Each of the three classes has color and price and this lets us write some comparison
logic:

74
Chapter 9: Classes in Depth

const allProducts: any[] = [].concat(


new Shirt(),
new Shirt(),
new Pants(),
new Shoe(),
new Pants(),
new Shirt());

const Recommend = function(minPrice, maxPrice, requestedColor) {


return allProducts.reduce(function(prev, curr) {
if ((curr["color"] === requestedColor) ||
(curr["price"] >= minPrice && curr["price"] <= maxPrice)) {
return prev.concat(curr);
}
}, []);
}

console.log("Recommended for min/max price of 10/20 and color = blue:",


Recommend(10, 20, "blue"));

In this code, we build up a random array of products. The code doesn't show it, but you can
easily pretend that each object is initialized with appropriate data.

The code defines a function, Recommend and that function iterates (via reduce ) over the
collection of products, extracting those that match the user's criteria.

This works well enough, but it's really pretty awful overall. There's an any array. It's
referencing object properties via bracket notation. If we accidentally put a colorless product
in the allProducts array, such as bottled water, the code throws a runtime error or returns an
undefined value. Even if we add a new product, such as a scarf, we need to be very careful
that we follow the expected naming convention. This, for instance, will cause a runtime error:

class Scarf {
public Color: string;
public price: number;
public length: number;
constructor();
}

It would fail because color is capitalized in the Scarf object but the Recommend function
expects lower case names.

We can avoid all this trouble by using interfaces to define required properties:

75
Chapter 9: Classes in Depth

interface IRecommendable {
color: string;
price: number;
}

So far, this looks a lot like the interfaces discussed earlier in the book. However, we can also
apply interfaces to classes:

interface IRecommendable {
color: string;
price: number;
}

class Scarf implements IRecommendable{


public color: string;
public fabricType: string;
public price: number;
public length: string;
constructor() { }
}

The implements keyword tells TypeScript that Scarf objects always minimally define
color and price properties. They can define more properties and as you can see, they

do. However, they must at least define those two.

We can make other "Recommendable" objects and by doing this, we can now enjoy some
intellisense support. Consider this refactored code:

76
Chapter 9: Classes in Depth

interface IRecommendable {
color: string;
price: number;
}

class Scarf implements IRecommendable{


public color: string;
public fabricType: string;
public price: number;
public length: string;
constructor() { }
}

// Product Displays can't be recommended so doesn't implement the interface.


class ProductDisplay {
public name: string;
public location: string;
constructor() {}
}

class Sneaker implements IRecommendable {


public color: string;
public inseam: number;
public waist: number;
public price: number;
}

const allRecommendableProducts: IRecommendable[] =


[].concat(
new Sneaker(),
new Sneaker(),
new Scarf(),
new Sneaker(),
new Sneaker(),
new Scarf());

const GetRecommended = function(minPrice, maxPrice, requestedColor) {

return <IRecommendable> allProducts.reduce(


function(prev: IRecommendable[], curr: IRecommendable) {
if ((curr.color === requestedColor) ||
(curr.price <= maxPrice && curr.price <= maxPrice)) {
return prev.concat(curr);
}
}, []);
}

const RecommendedItems = GetRecommended(10, 20, "blue");

console.log("Recommended for min/max price of 10/20 and color = blue:", Recommend(10,


20, "blue"));

77
Chapter 9: Classes in Depth

This code has many advantages over the earlier, non-interface style approach:

allRecommendableProducts contains a collection of objects ( IRecommendable[] ) each of

which is guaranteed to hold a price and color property.


If we try to add another object, such as ProductDisplay to that collection, the IDE will
warn us that it does not meet the interface requirements of the collection's objects. This
means that our code can safely assume the object properties are present.
We can reference the color and price properties using dot notation inside the reduce
function. In fact, the IDE even gives helpful intellisense hints.

Here's a video showing the whole thing:

(If you can't see the video, try clicking here or type this URL into your web browser:
https://youtu.be/97u6yaGJ1T4.)

Classes, Interfaces and Methods


In addition to defining data requirements, you can define required methods. Let's explore
this in the context of a data export. You've modeled a collection of products as objects and
you want to allow an end user to export those products out to an Excel spreadsheet. Excel
works great with comma separated lists, so if your objects can create a comma-separated
version of themselves, then it's a piece of cake to export that out and let Excel do its magic.

This wouldn't be very hard to do in a generic way using plain JavaScript, so let's complicate
matters a little bit by introducing a bit of security. Some objects contain sensitive information,
such as cost and you want to restrict access to that property based on the user's role (e.g.
"operator", "supervisor", "administrator"). Lastly, we're not only worried about the cost
property. Some products, but not all, are subject to inventory control measures. In these
cases, rather than providing the product's actual inventory-on-hand, we need to show a
message, "contact sales."

78
Chapter 9: Classes in Depth

We could write a big messy CSV generator that generically iterates over object properties
and then litter it with a bunch of if/then/else statements. Let's instead delegate the field level
logic to the product objects themselves.

Here's a moderately complex example:

interface StandardProduct {
name: string;
description: string;
}

interface SecuredFieldsItem {
GetAllowedFieldNames: (requestedByRole: string) => string[];
// NOTE: requestedBy would normally be a more complex object.
}

class Fidget implements StandardProduct, SecuredFieldsItem {

public name: string;


public description: string;
public inventory: number;
public weight: number;
public recommendedAge: number;
public cost: number;

constructor() {};

public GetAllowedFieldNames(requestedByRole: string) : string[] {


const minFields = ["name", "weight", "recommendedAge", "description", "invento
ry"];
if (requestedByRole === "Price Admin") {
return minFields.concat("cost");
}
return minFields;
}
}

class HotItem implements StandardProduct, SecuredFieldsItem {


public name: string;
public description: string;
public features: string[];
public inventory: number;
public cost: number;

constructor() {};

public GetAllowedFieldNames(requestedByRole: string) : string[] {


const minFields = ["name", "description", "features"];
let allFields = minFields;
if (requestedByRole === "Price Admin") {
allFields = allFields.concat("cost");
}

79
Chapter 9: Classes in Depth

if (requestedByRole === "Inventory Admin") {


allFields = allFields.concat("inventory");
}
return allFields;
}
}

function getGeneratedCsv(forProducts: SecuredFieldsItem[], forRoleLabel: string) {


return forProducts.reduce( (prev: string[], curr: SecuredFieldsItem) => {
const result = getFormattedCsvRow (curr, curr.GetAllowedFieldNames(forRoleLabe
l));
return prev.concat(result);
}, []);

function getFormattedCsvRow(sourceItem: SecuredFieldsItem, fieldsToRetrieve: string[])


: string {
return fieldsToRetrieve.reduce( (csvFieldAsBuilt: string, currentField: string) =>
{
if (csvFieldAsBuilt.length < 1) {
return sourceItem[currentField];
}
return csvFieldAsBuilt + "," + sourceItem[currentField];
}, "");
}

// Pretend that these products are initialized with real data.


const allSecurableProducts = [].concat(
new HotItem(),
new Fidget(),
new Fidget(),
new HotItem());

const csvOutput = getGeneratedCsv(allProducts, "Inventory Admin");

One of the first things you'll notice is that the code defines two interfaces: StandardProduct
and SecuredFieldsItem . Then, both classes (Fidget and HotItem) implement both interfaces:

class Fidget implements StandardProduct, SecuredFieldsItem

Classes can implement more than one interface. All you do is define your interfaces as usual
and then implement each one, separating multiple interfaces with a comma.

Look at the SecuredFieldItem class:

80
Chapter 9: Classes in Depth

interface SecuredFieldsItem {
GetAllowedFieldNames: (requestedByRole: string) => string[];
// NOTE: requestedBy would normally be a more complex object.
}

Classes that implement the SecuredFieldsItem interface must implement a method,


GetAllowedFieldNames . That method must take a string input parameter and it must return an

array of strings. In a more realistic scenario, you would probably pass in some kind of object
representing the user as a whole, including his/her roles. This example uses hard coded
strings to simplify things.

As you can see, GetAllowedFieldsNames has its own independent implementation in each
class. Fidget is only concerned about users whose role is "Price Admin". HotItem products
perform an additional check for users with the "Inventory Admin" role.

getGeneratedCsv invokes GetAllowedFieldNames on each product. Note the function

signature:
function getGeneratedCsv(forProducts: SecuredFieldsItem[], forRoleLabel: string)

It can iterate over disparate products with significantly different properties because they
each implement the SecuredFieldsItem interface. Therefore, they will always have the
GetAllowedFieldNames method to invoke.

Finally, the helper function getFormattedCsvRow generates a properly formatted row of


comma separated data based on the current item and the allowed fields.

Inheritance
Like Java and C#, TypeScript supports hierarchical class structures. This allows you to
incrementally build complex classes by starting with a minimal "base" class and then
extending it to a new class. This new extended class is said to inherit the functionality of its
base class. "Extend" means to add new class members (properties and/or methods).

Let's demonstrate inheritance by means of US residency models. In this case, "resident"


means a person living permanently or temporarily in the US.

All residents have a name. They have a name irrespective of their residency type.

A temporary resident is a resident with two additional properties: A country of origin and the
date that they need to exit the country (i.e. when their visa expires).

A US citizen, like a temporary resident is just a resident with some additional properties - the
name of the city in which they were born.

81
Chapter 9: Classes in Depth

Based on this, we can infer a class hierarchy as follows:

Resident
|
-----------------------------
| |
v v

Temporary Resident US Citizen

Let's show some code. Here's a Resident :

class Resident {

private _name: string;


public get MyName() { return this._name; }

constructor(name: string) {
this._name = name;
}
}

This simplistic class defines a single private property, _name . It can only be set when it's
first created:

const Kelly = new Resident("Kelly");

It has one accessor (a getter) to retrieve the resident's name:

console.log(`Resident's name: ${Kelly.MyName}.`);

Here's the model for a temporary resident:

82
Chapter 9: Classes in Depth

class TemporaryResident extends Resident {


private _countryOfOrigin: string;
public get MyCountryOfOrigin() { return this._countryOfOrigin; }

private _requiredExitDate: Date;

constructor(name: string, countryOfOrigin: string, requiredExitDate: Date) {


super(name);
this._countryOfOrigin = countryOfOrigin;
this._requiredExitDate = requiredExitDate;
}

This model introduces new syntax, the extends keyword:

class TemporaryResident extends Resident {

This means that TemporaryResident shares the same members as Resident . In this case,
it's both the _name property, as well as the Resident's constructor.

Any class that extends another must always invoke the extended class' constructor via a call
to super :

constructor(name: string, countryOfOrigin: string, requiredExitDate: Date) {


super(name);
this._countryOfOrigin = countryOfOrigin;
this._requiredExitDate = requiredExitDate;
}

As you can see, it doesn't need to have the same signature as the extended class.
TemporaryResident takes three parameters. It passes one of those, name , to its extended

class' constructor via the super(name) call.

Lets round out the example with one more model, a U.S. Citizen:

83
Chapter 9: Classes in Depth

class USCitizen extends Resident {

private _cityOfBirth: string;


public get MyBirthCity() { return this._cityOfBirth; }

constructor(name: string, birthCity: string) {


super(name);

this._cityOfBirth = birthCity;
}
}

Just like a TemporaryResident , the USCitizen class shares the same class members as
Resident . It uses the extend keyword to define its parent class. USCitizen 's constructor

invokes its parent class' constructor, passing in the name: super(birthCity) .

Here's a full featured video that demonstrates this in great detail:

If you can't see the video, try clicking this link or type this URL into your web browser:
https://youtu.be/-P1uYVlYEc4).

Hiding and Exposing Class Members


We've already see how the public keyword and private keyword protect or grant access
to your class members, both properties and methods. Inheritance adds a small bit of
complexity and enables you to control access to class members via public/private as well as
a new data control keyword, protected :

public members may always be accessed up and down the hierarchy and from

outside the client (i.e. client code).

84
Chapter 9: Classes in Depth

private members may only be access from within the class itself. This means that

extended classes may not access their parents' private members.


TypeScript provides a new keyword, protected . Protected members act like both
public and private members. They are private to any external client code. They are
public from their point of definition and all extended sub-classes.

This bit of code should help clarify matters:

class BaseClass {
private _myPrivateProperty: string = "No one can see me except BaseClass.";
protected _myProtectedProperty: string = "Extended classes can see me.";
public MyPublicProperty: string = "Anyone can see and manipulate me.";
}

class ExtendedBaseClass extends BaseClass {

constructor() {
super();

// Next line would be an error since myPrivateProperty is private in BaseClass


//this._myPrivateProperty = "xyzzy";

// ExtendedBaseClass can access _myProtectedProperty.


this._myProtectedProperty = "I can change this value.";

// Public property values can always be accessed within and outside of the cla
ss.
this.MyPublicProperty = "I can also change this value.";
}
}

const myExtendedClass = new ExtendedBaseClass();


myExtendedClass.MyPublicProperty = "Set directly on the class via client code.";

// Error:
// myExtendedClass._myPrivateProperty =
// "This is not allowed since private properties cannot be read or written.";

// Error:
// myExtendedClass._myProtectedProperty =
// "This is also not allowed since it's protected.";

The code sample shows:

A class, BaseClass .
BaseClass defines three members: _myPrivateProperty , _myProtectedProperty and
MyPublicProperty .

It defines another class, ExtendedBaseClass . This extends BaseClass.


ExtendedBaseClass is allowed to access _myProtectedProperty and MyPublicProperty.

85
Chapter 9: Classes in Depth

ExtendedBaseClass may not access _myPrivateProperty.


Some client code defines a new const variable, myExtendedBaseClass . It holds a
reference to an instance of ExtendedBaseClass.
The client code is able to access the instance's MyPublicProperty but is prevented from
accessing either the private or the protected properties.

Abstract Classes
Abstract classes round out TypeScript's support for hierarchies of this nature. An abstract
class looks and feels like a standard class with a key exception: abstract classes may never
be instantiated. If JavaScript is your first and primary programming language, this may seem
strange. However, abstract classes, along with interfaces, enable developers to express
many common software design patterns naturally and gracefully. Let's consider an example.

Imagine that you are a writing a game. Players place different types of military bases (e.g.
"Army", "Navy") on a two dimensional map. Bases share some common features, like
"name" but diverge from each other in important details. Army bases consist of soldiers
while navy bases consist of ships. Lastly, at run-time, players can "activate" a base. This
triggers the base to do something meaningful in the game. Here's a naive way to model it:

86
Chapter 9: Classes in Depth

interface Activatable {
ActivateSelf: () => void;
}

class NaiveBase {
private _myName: string;
public get Name() { return this._myName; }
constructor (name: string) {
this._myName = name;
}
}

class NaiveArmyBase extends NaiveBase implements Activatable{


private _totalSolders: number;
public get TotalSolders() { return this._totalSolders; }

constructor(name: string, totalSolders: number) {


super(name);
this._totalSolders = totalSolders;
}

public ActivateSelf() {
throw "Not yet implemented";
}
}

class NaiveNavyBase extends NaiveBase implements Activatable {


private _totalShips: number;
public get TotalShips() { return this._totalShips; }

constructor(name: string, totalShips: number) {


super(name);
this._totalShips = totalShips;
}

public ActivateSelf() {
throw "Not yet implemented";
}
}

const naiveArmyBase = new NaiveArmyBase("First army base", 100);


const naiveNavyBase = new NaiveNavyBase("First navy base", 3);

// This is allowed but makes no sense:


const someOtherBase = new NaiveBase("what kind of base is this?");

By now, this is pretty straight-forward. A NaiveBase class holds a private property, _myName
and provides an get accessor to retrieve the value. Two other classes extend it and add their
own properties: NaiveArmyBase and NaiveNavyBase .

87
Chapter 9: Classes in Depth

Both the army an navy base classes implement the Activatable interface, albeit in this
example, each class' ActiveSelf() method simply throws an exception.

There is a problem with this modelling approach: there's no such thing as plain vanilla
NaiveBase. Players never create vanilla bases, they always create a specific kind of base.
However, there's nothing stopping the code from doing that.

There's another problem here as well. This approach forces us to implement the
Activatable interface on every class. We could implement it on the base class, but that just

compounds the first problem - now we've implemented an interface on a class we should
never instantiate.

Abstract classes solve this problem for us. Here's the code re-written using an abstract
class:

88
Chapter 9: Classes in Depth

interface Activatable {
ActivateSelf: () => void;
}

abstract class AbstractBase implements Activatable{


private _myName: string;
public get Name() { return this._myName; }

constructor (name: string) {


this._myName = name;
}

abstract ActivateSelf(): void;


}

class ArmyBase extends AbstractBase {


private _totalSolders: number;
public get TotalSolders() { return this._totalSolders; }

constructor(name: string, totalSolders: number) {


super(name);
this._totalSolders = totalSolders;
}

public ActivateSelf() {
throw "Not yet implemented";
}
}

class NavyBase extends AbstractBase {


private _totalShips: number;
public get TotalShips() { return this._totalShips; }

constructor(name: string, totalShips: number) {


super(name);
this._totalShips = totalShips;
}

public ActivateSelf() {
throw "Not yet implemented";
}
}

const armyBase = new ArmyBase("First army base", 100);


const navyBase = new NavyBase("First navy base", 3);
const anotherArmyBase: Activatable = new ArmyBase("Second army base", 250);

// Compiler throws an error - abstract classes can not be instantiated:


const someOtherKindOfBase = new AbstractBase("what kind of base is this?");

89
Chapter 9: Classes in Depth

This example introduces the abstract keyword. We now have an abstract class, Base .
This abstract class implements the Activatable interface. In doing so, you can see another
characteristic of TypeScript's abstract functionality: you may mark classes and class
members as abstract. (In fact, you must mark the class abstract if it contains any abstract
members). The Activatable interface requires a method, ActiveSelf . However, this method
only makes sense for "real" bases - army and navy bases. Hence, we mark the ActivateSelf
method itself as abstract:

abstract ActivateSelf(): void;

This abstract ActivateSelf method meets the requirements of the Activatable interface. This
is perfect since a vanilla "base" can't meaningfully activate itself - only army and navy bases
can do that. At the same time, it forces subclasses to implement the method. This is good for
two reasons:

1. You can't forget to do it since the IDE and compiler won't let you.
2. Since the subclasses implement the interface, we can write code that leverages their
type as Activatable where and when we need to.

The abstract Base class shows another feature: Abstract classes can define non-abstract
class members. Since every base has a name, regardless of base type, it makes sense to
define a concrete _myName property and associated getter. Sub-classes inherit these
concrete class members (properties and methods) just like they do with concrete classes.

The army and navy bases extend the abstract class just as if it were a concrete class using
the same extends keyword.

Wrapping up the example, you can see that newing up army and navy bases works the
same way as it does in the naive example:

const armyBase = new ArmyBase("First army base", 100);


const navyBase = new NavyBase("First navy base", 3);

Since both types of bases implement Activatable, you can do this:

const anotherArmyBase: Activatable = new ArmyBase("Second army base", 250);


const activatableNavyBase = <Activatable> navyBase;

Let's put it all together in a video:

90
Chapter 9: Classes in Depth

(If you can't view that video, try clicking tis link or typing this url into your web browser:
https://youtu.be/ska4WEeG3pM.)

Further Reading
I wrote a blog post that combines unions, Rest parameters and interfaces (wrapped
inside class) that implements a general purpose logger function:
https://blog.hellojs.org/simple-javascript-logger-in-typescript-demonstrating-interfaces-
union-types-and-rest-parameters-6efc5aee2c97

Summary
The previous chapter gave you a sip and this chapter turned on the fire hose.

Use interfaces to define both the shape of data and the shape of classes. In this case,
"shape" means required class members (both methods and properties).

Classes implement interfaces. Classes may implement multiple interfaces.

TypeScript allows you to create hierarchies. A class can extend another class and it, in turn,
may be extended. A given class can only extend one other class.

A special kind of class, the Abstract Class, can never be instantiated but otherwise looks and
feel the same as non-abstract classes. Abstract classes can (and often do) implement
interfaces and they can even define concrete members (properties and methods).

We're nearly done with classes. The next chapter provides te final word on classes, as well
as introducing the final bit of typing TypeScript offers - Generics.

91
Chapter 9: Classes in Depth

1. Get accessors are well-used when you want to make a property available to client

code but you don't want to let that client code edit the value. In this case, you'd define a
private variable paired with its own Get accessor but no Set accessor. Don't create a
private variable and then pair it with a public getter and setter. In that case, you may as
well just keep it public. ↩
2. Since you're still reading at this point, it's probably safe to say that you're satisfied

that TypeScript is pretty useful. If you're still on the fence, consider how you'd address
this same issue with plain JavaScript. If you needed to make a change of this nature, it
would be much more difficult to achieve given that you can't get the same kind of great
tooling support. You can't force a syntax error the same way. You have to rely on global
search and/or find/replace. Not very fun. ↩

92
Chapter 10: Generics

Generics
TypeScript supports a programming construct called generics. TypeScript generics allow you
to write code that operates against broad swathes of classes and interfaces without giving
up strong typing. You write your code to execute against types (classes and interfaces), as
opposed specific, concrete classes. Once written, you access this generic code by providing
a concrete type at runtime. Let's consider an example.

Imagine that you are developing a game and storing and retrieving game information to/from
a database. This means you have to implement the classic Create, Read, Update and
Delete operations (CRUD) for the various objects in the game. Here's some high level code
that beings to implement the game and this logic:

class Game {
private _gameState: any;

public CurrentPlayerIndex: number;


public Players: Player[];

constructor(initialGameState: any) { this._gameState = initialGameState;}

class Player {
private _playerState: any;

public PlayerName: string;


public PlayerScore: number;

constructor(initialPlayerState: any) { this._playerState = initialPlayerState; }


}

class GameStateDBHelper {

public CreateNewGame(): Game {


// Initialize a new Game object and save it to the back end database.
// Return the empty game.
return new Game(null);
}

public LoadGame(query: string): Game {


// Use supplied query to load some game state from the database.
// Convert that to a GameState object
// return it
return new Game(null);
}

93
Chapter 10: Generics

public SaveGame(gameToSave: Game): boolean {


// Marshall game state and save it to the back end database.
return true; // indicates successful save
}

public DeleteGame(gameToDelete: Game): boolean {


// Issue database command to delete game state.
return true; // indicates successful deletion
}

const gameHelper = new GameStateDBHelper();


const newGame = gameHelper.CreateNewGame();
const oldGame = gameHelper.LoadGame("a database query");

const didSaveGame = gameHelper.SaveGame(oldGame);


const didDeleteGame = gameHelper.DeleteGame(newGame);

The code defines three classes:

Game : This is the game object itself, keeping track of overall game state, including a list

of players and the currently active player.


Player : Represents a player in the game. Players also have some state information,

although its different than a Game .


GameStateDBHelper : A utility class that provides input/output operations and supports all

four CRUD operations for the Game object.

GameStateDBHelper defines four public methods, one for each of the CRUD operations.

These each take commonsense input parameters and return commonsense results.
Consider LoadGame :

public LoadGame(query: string): Game {


// Use supplied query to load some game state from the database.
// Convert that to a GameState object
// return it
return new Game(null);
}

LoadGame is passed a query (think "select * from Games..."). It parses the result and returns

back a new Game object. Obviously, there's a lot of hand waving going on in the example,
but hopefully the concept is clear.

The DB helper object makes it easy to execute the CRUD operations as needed:

94
Chapter 10: Generics

const gameHelper = new GameStateDBHelper();


const newGame = gameHelper.CreateNewGame();
const oldGame = gameHelper.LoadGame("a database query");

const didSaveGame = gameHelper.SaveGame(oldGame);


const didDeleteGame = gameHelper.DeleteGame(newGame);

Despite the clarity and strong-typed goodness, this approach is nonetheless problematic.
We already know we'll want another database-backed entity - Player . If we simply follow
the current approach, we end up creating a new helper function, PlayerStateDBHelper . It
has to provide the same CRUD functions and each one shaped almost identically to
GameState. "Shape" in this case means:

Looking up database connection information.


Accessing the database
Executing some common command that varies only in small details from one object to
another
Managing errors
Returning success/fail messages

We can mitigate most of that using TypeScript's generic functionality. Here's how it would
look like:

95
Chapter 10: Generics

interface DBBackedEntity {
TableName: string;
}

class GameState implements DBBackedEntity {


private myDBTableName: string;
public get TableName(): string { return this.myDBTableName; }

public CurrentPlayerIndex: number;


public AllPlayers: GamePlayer[];

constructor(someGameState: any) {
this.myDBTableName = "Games";
}

class GamePlayer implements DBBackedEntity {


private myDBTableName: string;
public get TableName(): string { return this.myDBTableName; }

public PlayerName: string;


public Score: number;

constructor(somePlayerState: any) {
this.myDBTableName = "Players";
}
}

class DBHelper<T extends DBBackedEntity> {


public CreateRecord() : T { return null; }
public ReadRecord(query: any): T { return null; }
public DeleteRecord(basedOn: T): boolean { return true; }
public UpdateRecord(basedOn: T) : boolean { return true; }
}

const gameStateHelper = new DBHelper<GameState>();


const gamePlayerHelper = new DBHelper<GamePlayer>();

const newPlayer = gamePlayerHelper.CreateRecord();


console.log(`New player score: ${newPlayer.Score}.`)

const existingGameState = gameStateHelper.ReadRecord("some query goes here");


const newGameState = gameStateHelper.CreateRecord();

const deleteResult = gameStateHelper.DeleteRecord(newGameState);


const updateResult = gameStateHelper.UpdateRecord(existingGameState);

Generics introduce some new syntax and leverage existing concepts (like interfaces) in new
ways.

96
Chapter 10: Generics

The code first defines a new interface, DBBackedEntity . This interface requires a single text
field, "TableName". This obviously maps to a database table via its name.

It then creates two models for the Game and its Players respectively. Each of them
implements the DBBackedEntity interface and assigns a database table name via the
object's constructor.

The DBHelper class introduces the generics syntax:

class DBHelper<T extends DBBackedEntity> {


public CreateRecord() : T { return null; }
public ReadRecord(query: any): T { return null; }
public DeleteRecord(basedOn: T): boolean { return true; }
public UpdateRecord(basedOn: T) : boolean { return true; }
}

This syntax, <T extends DBBackedEntity> effectively says, "The DB helper class works
against any type (class) that implements the DBBackedEntity interface." When client code
instantiates an instance of DBHelper, it will specify a value for that parameter, T . These
two lines show how to pass a value for T :

const gameStateHelper = new DBHelper<GameState>();


const gamePlayerHelper = new DBHelper<GamePlayer>();

When working with generics, we supply type parameters via angle brackets:
DBHelper<GameState> and DBHelper<GamePlayer> . TypeScript replaces the T parameter in

the DBHelper class with GameState and GamePlayer respectively.

Further Reading
I wrote a lengthy blog post describing how to use generics to implement a binary
search. You can read that here: https://blog.hellojs.org/implement-binary-search-in-
typescript-using-generics-with-useful-refactorings-a4bcda932d7.

Of particular interest to React developers, this article describes how to use default
values with generics: https://blog.mariusschulz.com/2017/06/02/typescript-2-3-generic-
parameter-defaults. Note that it's written in the context of a React application but the
feature is not tied to React.

Summary

97
Chapter 10: Generics

This chapter on generics brings the main body of of Yet Another TypeScript Book to a close.
The next chapter suggests some additional reading and videos that you may find of interest

98
Chapter 11: Continue Learning

Continue Learning
I hope that you had a pleasant and enriching experience reading this book and watching the
videos. It sure was for me!

I think I've covered close to 80 percent of the language. In this chapter, I'm going to point out
a few additional resources that will bridge that 20 percent gap (at least partially). In addition,
as I wrote way back in the intro, I think that it's important to differentiate instruction. Some of
the links I reference here (and did in the various "Further Reading" sections) duplicate some
of this book's content, albeit in a different voice. That's a good thing :). Here they are:

General Topics
I frequently referred to the SOLID acronym. Here's a good article to get you started on
these concepts: SOLID: https://dev.to/samueleresca/solid-principles-using-typescript?
utm_content=buffer2e11d&utm_medium=social&utm_source=twitter.com&utm_campaig
n=buffer
A useful twitter search to keep you up to date with the latest developments:
https://twitter.com/search?q=%23typescript&src=typd&lang=en
A collection of good overall links to TypeScript goodness:
https://www.bennadel.com/blog/recent-blog-entries.htm
Free online class from edx: https://www.edx.org/course/introduction-typescript-2-
microsoft-dev273x?wt.mc_id=DX_883248&utm_source=t.co&utm_medium=referral

Testing TypeScript
Use the Intern framework: https://www.sitepen.com/blog/2015/03/24/testing-typescript-
with-intern/?utm_content=55755016&utm_medium=social&utm_source=twitter

TypeScript in the Wild


There are many open source projects written in TypeScript. Here's a small selection. Take
some time to read through their code, get a sense for their project structure and how they
use TypeScript features:

Multi-player game: https://github.com/code0wl/Multiplayer-Phaser-


game/tree/develop/src/client

99
Chapter 11: Continue Learning

Typescript cookbook: https://schneids.net/introducing-the-typescript-cookbook/


Inversify, a framework for addressing the I in SOLID:
https://github.com/inversify/InversifyJS
TypeORM, a TypeScript object relational mapper: https://github.com/typeorm/typeorm
The EVE programming language: https://github.com/witheve
Widgets and gadgets: https://github.com/phosphorjs/
Navalia, an end to end testing framework: https://github.com/joelgriffith/navalia

TypeScript and Node


Writing a module in node using TypeScript: https://www.twilio.com/blog/2017/06/writing-
a-node-module-in-typescript.html

Converting Plain JS to TypeScript


Convert a React component to typescript: https://devsandbox.io/articles/converting-
react-to-typescript/

Advanced Topics
Mixins: https://blog.mariusschulz.com/2017/05/26/typescript-2-2-mixin-classes.
Decorators: https://cabbageapps.com/fell-love-js-decorators/

The Grand Summary


This concludes Yet Another TypeScript Book! Thank you for reading!. If you found it helpful
(or not!) I hope you'll send me some feedback or even contribute some new content. I
already wrote about that in the introduction, but here it is again while I have your attention :)

100
Chapter 11: Continue Learning

I think that most authors, and I count myself among them, derive immeasurable
satisfaction from reader feedback. If you'd like to contribute to the book in a non-
material, spiritual way (like "attaboy!" or "Dear Lord, what fresh hell have you visited
upon the world with this book!"), the easiest thing is to simply send me a note to
galvin.paul@gmail.com. It would be helpful if you put the words "TypeScript Book" in
the subject, but certainly isn't required. I always get a little extra pep in my step when
someone leaves a comment on one of my blog posts or reaches out by email. It's better
than being paid7.

I have long been impressed, interested and even a bit envious of the You Don't Know
JavaScript series. Kyle Simpson obviously hit a nerve and he has a really thriving
Github project going. I have, in fact, tried to follow his model. If you'd like to participate
in a more material way, hit up this book's github site, https://github.com/pagalvin/tsbook:

Star the project


Log some issues
Correct problems you find and issue a pull request
Suggest and even write entire new areas of content and issue a pull request

I will make every effort possible to respond to your emails, review and manage github
issues and honor high quality pull requests.

Thank you and best of luck.

101

You might also like