Total TypeScript
Essentials

Total TypeScript: Essentials is a book for devs of all levels to learn advanced type manipulation and real-world application development patterns in TypeScript.

Start Reading

JavaScript's developer experience has left you wanting more. With only basic autocompletion, limited refactoring support, and the constant fear of runtime errors, it was time to make the change.

You have chosen wisely.

But getting going with TypeScript isn’t as straightforward as it seems:

There's tooling to configure, and the type system requires a different way of thinking about variables and functions than you used in JavaScript. Concepts like generics, mapped types, and type guards can be confusing, and the TypeScript compiler can be unforgiving when it comes to type errors.

What you need is a clear path to becoming a TypeScript Wizard– a well-structured, comprehensive guide to the most important TypeScript concepts and features, with hands-on practice at every step of the way.

Total TypeScript: Essentials is exactly what you've been looking for.

This book guides you on a journey from novice to confident expert, covering everything from installing the TypeScript compiler to advanced type manipulation and real-world application development patterns.

You'll learn how to configure your project for optimal type checking, design expressive types that model your domain, and create flexible utility functions that can be used in any context.

Here's what you'll find inside:

Introduction to TypeScript

Kickstart Your TypeScript Setup

In this first chapter, you will set up your TypeScript environment following the same specifications that are used by the pros. You'll install essential tools like the TypeScript compiler, Visual Studio Code, and the PNPM package manager that acts as a more efficient alternative to NPM. Once the tools are setup, you'll take a first look at how the TypeScript compiler works together with the editor to provide instant feedback on your code.

Whether you're brand new to TypeScript or a seasoned dev, this chapter will set you up for success in the rest of the book and beyond.

IDE Superpowers

There's a lot more to development than just writing code. You have to keep track of imports, dig through documentation to remember how things work, and navigate through mazes when it's time to refactor.

Luckily, Visual Studio Code offers a ton of features and extensions that work seamlessly with the TypeScript compiler to make your life easier. In this section, you'll learn how to go beyond the basics of autocompletion and syntax highlighting to take full advantage of what VS Code has to offer. You'll pick up tricks for quickly navigating and refactoring your codebase, import multiple variables from modules with a single action, and see how TSDoc can be used to generate useful documentation right in your editor.

If you weren't using VS Code before, you'll wonder how you ever lived without it!

TypeScript in the Development Pipeline

The TypeScript compiler turns your code into the JavaScript that browsers can understand. Up to this point in the book, the tsc command has been run manually every time a code change is made. Of course, this isn't how things work in the real world, so it's time to make some changes and understand TypeScript's role in the build process.

In this chapter, you'll learn to configure your project to automatically compile your TypeScript code, while properly organizing the emitted files. You'll also learn how to set up tsc to work with modern frontend build tools like Vite to support features like Hot Module Reloading for the browser, or to act as a linter if other tools are already in place. Finally, you'll practice setting up type checking in a CI/CD pipeline to prevent deployment if type errors are found.

By the end of this chapter, you'll have a solid understanding of how TypeScript fits into the development pipeline, and how to configure it to work seamlessly with your team's preferred tools.

TypeScript Fundamentals

Essential Types and Annotations

JavaScript's flexibility is a double-edged sword. While it allows you to quickly write code that works, it also leaves you open to runtime errors and subtle bugs that can be tricky to track down. This is why TypeScript's type system is so powerful. By adding type annotations to your code, you can catch many of these errors before they happen.

This section is all about the types at TypeScript's foundation. You'll start by annotating primitive types like numbers, strings, and booleans before moving on to more complex scenarios like function parameters, return values, and async code. You'll also learn how to model data with object types, arrays, and tuples, as well as responsibly using the any type and working with data from external sources. Along the way, you'll also see how TypeScript's type inference works to take some work off your plate.

It may take a little more time upfront to add type annotations to your code, but it will result in you being more confident in your code. Besides, the productivity boost from your development environment will more than make up for it!

Unions, Literals, and Narrowing

Every JavaScript developer has run into issues where an unexpected null or other mismatched type causes a crash. Fortunately, TypeScript has a few tricks up its sleeve to work around these issues.

The first tool you'll learn in this chapter is the union type, which specifies that a value can be one of several types. This idea also extends to literal types, which allow you to define a set of allowed values for strings. The combination of union and literal types can be used to more effectively model complex data structures like API responses, while also supporting more useful autocompletion in your editor. You'll also begin to practice type narrowing, which helps TypeScript refine your types through conditional checks and type guards.

If you've ever been stuck on unknown or never errors in your TypeScript, this chapter is for you!

Objects, Classes, and Mutability

The Object Type

Objects are everywhere, but modeling them in TypeScript can be a challenge. You might feel like you're repeating yourself with redundant properties across types, or struggling to work with dynamic data that doesn't fit into a fixed structure.

This chapter begins with a look at the nuances of type vs. interface in TypeScript, and when to use each. You'll see how interface extension helps you to create clean inheritance hierarchies while eliminating duplicate properties. You'll get hands-on practice working with built-in TypeScript utility types like Pick, Omit, Record, and Partial for creating your object types. Finally, you'll work with advanced techniques like distributed type helpers and create your own custom type helpers to solve complex typing challenges.

Understanding Mutability

There's a surprising nuance to understanding how TypeScript handles mutability. It ends up that the let and const keywords don't necessarily work the way you think they do, and the way you declare your variables has a big impact on how TypeScript behaves.

In this chapter, you'll see how immutability is an illusion unless you take specific steps to enforce it with specific TypeScript keywords and assertions. You'll also practice working with readonly arrays, and using the ts-reset tool for smoothing out some of TypeScript's rough edges.

By practicing with these techniques and concepts, you'll develop the mental model for how mutability affects type inference and make informed decisions about when to use immutable data structures in your code.

Classes in TypeScript

Classes are common in TypeScript, so it's important to be familiar with them. However, they can be a source of confusion. There are multiple ways to create classes and initialize their properties, and the syntax used for method definitions and access modifiers can impact how your classes are used.

These topics are all discussed in detail in this chapter. You'll practice various techniques for creating classes and defining their methods and properties, and see how TypeScript applies Object-oriented Programming design patterns and principles. You'll also build the mental model for the this keyword and how it behaves in different contexts.

TypeScript-Only Features

By this point in the book you're well on your way to being a solid TypeScript developer. However, there are some features specific to TypeScript that you need to be aware of.

Enums, namespaces, and parameter properties all have their place in TypeScript, but aren't without their quirks. In this chapter, you'll learn how these features work, as well as when you should opt for more JavaScript-aligned alternatives that use built-in types.

Being able to critically evaluate TypeScript features and the best approach to your projects is a key skill in becoming a TypeScript wizard.

Working with the Compiler

Deriving Types from Values

You've heard it before, but repeating yourself in code is not only annoying but leaves you open to errors. In previous chapters you will have seen some techniques for increasing reusability when defining types, but that doesn't really help when working with external libraries or dealing with the back-and-forth between runtime values and their corresponding types. Wouldn't it be nice if the compiler could just figure out a value's type on its own?

With help from some special keywords, TypeScript can do just that. By using the typeof operator, you'll be able to capture the type from an existing value at runtime. Similarly, the keyof operator can be used to dynamically generate types based on objects. These are just two of the tricks that can be used for deriving types from values– there are also utilities for getting types from functions and async operations.

In this chapter, you'll learn several more techniques for helping TypeScript help you to reduce the amount of manual definitions you need to write in your code while keeping your types accurate and up-to-date with your data.

Annotations and Assertions

TypeScript's type inference is powerful, but it's not perfect. Sometimes you need to step in and help the compiler out and add some manual type annotations. However, you need to know when and where they should be added. If you add too many annotations, your code will become cluttered. If you add too few, you run the risk of missing out on the full benefits of the type system.

In this chapter, you'll strike the perfect balance between the two. You'll develop an eye for when type annotations are needed and when they're not, especially when function parameters and complex types are involved. You'll also learn how to use type assertions to tell TypeScript that you know more about a value than it does through the as keyword, non-null assertions, and the satisfies operator among other topics.

Building the mental model of when and how to use type annotations and assertions will help you to write better TypeScript code.

TypeScript: The Weird Parts

Many of TypeScript's features and error messages are relatively straightforward, but there are times where things get weird.

Objects ending up with extra properties, unions of functions behaving unexpectedly, and iterating with Object.keys lacking type safety are just a few of the examples you'll work through in this part of the book. There are times when you have to do things a bit differently in order to get the results you want, and this chapter will help you to understand why.

Through compiler configuration, type assertions and annotations, and helper utilities, you'll be equipped to solve issues like these when you encounter similar situations in your own projects.

Understanding the Environment

Modules, Scripts, and Declaration Files

TypeScript files can be treated as either modules or scripts, and it's important to understand the distinctions between the two. Depending on how you write your code and configure your project, you can end up with different behavior that may not be desirable.

In this chapter, you'll work through the process behind TypeScript module resolution and remove the "magic" behind ambient contexts and global type declarations. You'll also learn how to provide type definitions by using .d.ts files.

Type safety is great, but what about when you're working with code that you don't control? This might be a third-party library, environment variables, or even the DOM. Without your help, the TypeScript compiler will default to using the any type, which isn't exactly a great solution.

By working through some common type errors, this chapter will show you how to provide types for libraries and other code you don't control. You'll configure TypeScript for optimal type resolution, and learn how declaration merging can be used to extend global interfaces like Window.

Understanding type definitions and the ambient context will allow you to optimize type-checking performance while ensuring your codebase remains as organized and type-safe as possible.

Configuring TypeScript

The tsconfig.json file is at the heart of every TypeScript project. It's where you configure the behavior of the TypeScript compiler, and where you can set options that affect how your code is compiled and checked. It also has about a thousand different options that don't always have obvious meanings.

Building onto the configuration options discussed in previous sections, this chapter provides you with the best practices for configuring TypeScript for different types of projects. You'll see how module resolution strategies affect your imports, set options that impact "Jump to definition" in your editor, and how to prevent your projects from becoming cluttered.

Whether you're working on small personal projects, shared libraries, or enterprise-level monorepo setups, this section will ensure you have the right TypeScript configuration for the job.

Advanced Application Development

Designing Your Types

Types are more than just their annotations– they're the blueprint for your application's logic. Poorly designed types can lead to code that's tough to maintain, hard to understand, and prone to errors. There's so much more to types than the built-in types and helper utilities that TypeScript provides out of the box!

This chapter will have you thinking differently about your types. You'll learn how features like generics, template literals, and mapped types can be used to represent real-world concepts and relationships in your code. The types you create will contain their own business rules and constraints, allowing you to create a centralized source of truth that can be accessed from anywhere in your codebase.

The concepts you'll practice with here are directly applicable to the real world. You'll learn how to build generic type utilities for API responses, design expressive error types, and learn techniques for generating code based on your type definitions.

Domain modeling is one of the most important skills for a TypeScript developer to have, and this chapter will set you on the right path.

The Utils Folder

When you want some code to be shareable but don't want to build a full-blown library, you can throw it into a utils folder. However, there are a host of cryptic errors and edge cases that can be encountered when trying to create the most flexible and reusable utilities. You can end up with unknown types, have underwhelming type inference, or get stuck when trying to write functions that can accept different types of data and argument patterns without sacrificing type safety.

All of these issues are addressed in this chapter. You'll practice working with generics, function overloads, and using type predicates to ensure TypeScript is able to precisely infer the types of your utility functions. You'll also learn how to create type-safe APIs that can be used in a variety of contexts without sacrificing type safety.

These advanced techniques will change the way you think about writing utility functions in TypeScript. Stop the copy/paste/tweak cycle, and start creating reusable code that's flexible enough to handle any situation.


Unlock Your TypeScript Potential

After reading Total TypeScript: Essentials you'll gain the knowledge and skills to confidently work with TypeScript in any project. You'll have a deep understanding of the type system, the compiler, and how to design your types for maximum flexibility and maintainability in codebases of all sizes.

Now is the time to transform yourself into a TypeScript Wizard.

Get started with Total TypeScript: Essentials today!