Advanced TypeScript

Stefan Baumgartner

Stefan on Mastodon

More on TypeScript

You rarely learn Advanced TypeScript features just by themselves. Only when combined with other parts of the language and put in context they reveal their true power. And this is the focus of this guide.

This is a constantly updated collection of articles you can find on this site. Clustered by use-cases, detailing which technologies you use to achieve your goal.

While each article stands on its own I can recommend reading it like a book, as you see familiar concepts in a new light.

Ready? let’s go!

Last update: May 11, 2022 at 28.000 words!

Table of contents #

TypeScript and JavaScript #

TypeScript is a strict superset of JavaScript, and you can feel that with every line of TypeScript you write. I wrote a couple of articles looking at concepts in JavaScript and how they influenced the inner workings of TypeScript.

Boolean in JavaScript and TypeScript #

Boolean is a very boring type, but there’s actually a lot to it, especially if you want to learn how the type-space in TypeScript works.

You learn:

  • Union types
  • null and undefined as types
  • Value types (or literal types)

Symbol in JavaScript and TypeScript #

Symbols are a very unique type in JavaScript, as every created symbol can only exist once. TypeScript even has a keyword for that:

You learn:

  • How Symbols work
  • The unique keyword
  • Run-time enums!

this in JavaScript and TypeScript #

Sometimes when writing JavaScript, I want to shout “This is ridiculous!”. But then I never know what this refers to. All about this and how to type it.

You learn:

  • this!
  • this parameters
  • ThisParameterType, OmitThisParameter, ThisType helpers

void in JavaScript and TypeScript #

void is a very special case of undefined, that not only exists in TypeScript, but also in JavaScript!

You learn:

  • The relation of undefined and void, and how it stems from JavaScript
  • A bit on substitutability

Substitutability #

Substitutability is a concept in TypeScript that makes sure that functions that are passed as values are type-safe even though the signature is not 100% the same.

You learn:

  • Substitutability for void
  • Substitutability for functions with different function signatures

The constructor interface pattern #

Classes in JavaScript are little more than syntactic sugar for the old prototype and constructor function pattern that exists for ages. That’s why TypeScript also needs two types to describe classes!

You learn:

  • Two types for classes: Constructor function and prototype
  • The new keyword in types

Control Flow #

Control flow is one of the biggest concepts in TypeScript. TypeScript will take all the type information and all your branching statements (if and switch) to figure out what type your variables have at a certain part in your code. You can influence control flow.

Type predicates #

Type predicates are custom type guards where you can decide to make the type of your input more concrete. We look at an easy example to see what narrowing down means.

You learn:

  • Custom type guards
  • Union types

Type predicates for hasOwnProperty #

We create a custom hasOwnProperty function that changes the shape of the current object significantly!

You learn:

  • Records
  • Type predicates
  • unknown type

Assertion signatures #

Assertion signatures are similar to type predicates, but they work outside branching statements. In this example, we create our own defineProperty function where we can add properties to the type we originally defined.

You learn:

  • Conditional types
  • Assertion signatures
  • Property Descriptor

Unexpected intersections #

There are situations where you write regular, working JavaScript, but get errors thrown at you! They bring some funny intersection types along, where you might wonder what they mean. This article sheds some light on it.

You learn:

  • Index access types safe-guards
  • Function type unions
  • Binding generics

Ambient declarations #

TypeScript has a ton of built-in types, all describing what’s going on in the world of JavaScript. But what if something is missing? We can declare our environment without having the actual code to it. This is what we do in this example.

Improving Object.keys #

This is a direct continuation of the last two examples in the last chapter. This time, we posh up Object.keys a little, and we are not creating a helper function, but patch the original ObjectConstructor.

You learn:

  • Conditional types
  • The keyof operator
  • The ObjectConstructor interface
  • Interface declaration merging

Array.prototype.includes on narrow types #

Similar to the two above, you are going to check if you can modify Array.prototype.includes to work in situations where your array is alrady very narrow!

You learn

  • Interface declaration merging
  • Type predicates
  • Type assertions

Ambient file modules #

What if the file you import isn’t a JavaScript or TypeScript file? This can happen in Webpack, for example. Thankfully, TypeScript has a way to describe any file and which types to expect.

You learn:

  • Wildcard module declarations
  • Module declarations for SVG, CSS, MDX, and more!

Extending JSX Elements #

What if you need to use a web component in JSX, but you get type errors because React doesn’t know about the components you use? You can extend the React types that you import with the information for your environment.

You learn:

  • Module re-declarations
  • Namespace declaration merging
  • Interface declaration merging

Augmenting global #

Type information for all the DOM APIs and window is stored in the global object. If we miss something, we can add info to the global namespace. We do this by adding a DOM API that will appear at some time in TypeScript once it’s stable enough: Resize Observer.

You learn:

  • WIDL
  • The global namespace

Working with Generics #

Generics are immensely powerful in TypeScript, especially combined with conditional types. Here we look at some

Type maps #

Type maps are a container type that contains a set of key-value entries that are very similar to plain JavaScript objects. Together with index types and mapped types, we can have a great lookup table!

You learn:

  • Index types
  • Type maps
  • Conditional types

Match the exact object shape #

TypeScript is a structural type system, which means that as long as all required properties of an object are available and have the correct type, TypeScript is happy. This can lead to some side effects if there are excess properties. Rarely, but possible! We work on a helper type that gets rid of excess properties.

You learn:

  • Conditional types
  • never type
  • Exclude

Variadic tuple types #

Variadic tuple types are a generic type pattern where you can define a sub-set of a tuple type in a generic type variable. What does that even mean? We take a good look at it in this article.

You learn:

  • Tuple types
  • Variadic tuple type generics
  • Conditional types

Union to Intersection type #

There are some cases where you need to transform a union type to an intersection type. We see how we can use distributive conditional types and contra-variance to do so.

You learn:

  • Union and intersection types
  • Distributive conditional types
  • Co-variance and Contra-variance

Pitfalls and workarounds #

Sometimes the type system doesn’t work as you think it should. There’s usually a good reason behind that.

The humble function overload #

The type system can be very complex. In some scenarios, some more traditional means can lead you to better goals than to use the latest and greatest features

You learn:

  • Conditional types
  • Variadic tuple types
  • When to make good use of function overloads

Unexpected intersections #

You try to do something in TypeScript that would work in JavaScript, but for some weird reason you get a type that is never or some other intersection that just doesn’t make sense. How can you work around this?

You learn:

  • Generics
  • Index access

Array.includes on narrow types #

Say you know exactly which values to expect. Now you want to check if some values from a broader set are part of it. This should work with Array.includes, except that it doesn’t.

You learn:

  • Union types
  • Type predicates
  • Generics

Iterating over objects #

Why can’t I just use Object.keys to access my object’s properties? There are several reasons. Let’s check them out.

You learn:

  • for-in loops
  • Generics

Typing catch clauses #

Your app throws an error, and you want to catch it. You know that only one error type can happen, still TypeScript insist it should be any or unknown. What’s the deal?

You learn:

  • Error handling

Stay up to date!

3-4 updates per month, no tracking, spam-free, hand-crafted. Our newsletter gives you links, updates on fettblog.eu, conference talks, coding soundtracks, and much more.