Book Teaser

How To Pass Types To Functions

Matt Pocock
Matt PocockMatt is a well-regarded TypeScript expert known for his ability to demystify complex TypeScript concepts.

Let's take a quick look back at the Array type we saw earlier.

Array<string>;

This type describes an array of strings. To make that happen, we're passing a type (string) as an argument to another type (Array).

There are lots of other types that can receive types, like Promise<string>, Record<string, string>, and others. In each of them, we use the angle brackets to pass a type to another type.

But we can also use that syntax to pass types to functions.

Passing Types To Set

A Set is JavaScript feature that represents a collection of unique values.

To create a Set, use the new keyword and call Set:

typescript
const formats = new Set();

If we hover over the formats variable, we can see that it is typed as Set<unknown>.

That's because the Set doesn't know what type it's supposed to be! We haven't passed it any values, so it defaults to an unknown type.

One way to fix this would be to pass some values to Set so it understands what type it's supposed to be:

typescript
const formats = new Set(["CD", "DVD"]);

But, we don't want to pass any values to it initially.

We can get around this by passing a type to Set when we call it, using the angle brackets syntax:

typescript
const formats = new Set<string>();

Now, formats understands that it's a set of strings, and adding anything other than a string will fail:

ts
formats.add("Digital");
 
formats.add(8);
Argument of type 'number' is not assignable to parameter of type 'string'.2345Argument of type 'number' is not assignable to parameter of type 'string'.

This is a really important thing to understand in TypeScript. You can pass types, as well as values, to functions.

Not All Functions Can Receive Types

Most functions in TypeScript can't receive types. A common example where you might want to pass a type is when calling document.getElementById.

typescript
const audioElement = document.getElementById("player");

We know that audioElement is going to be a HTMLAudioElement. This type comes from the DOM typings, which we'll talk about later.

So, it makes sense that we should be able to pass it to document.getElementById:

typescript
const audioElement = document.getElementById<HTMLAudioElement>("player");
Expected 0 type arguments, but got 1.2558Expected 0 type arguments, but got 1.

But unfortunately, we can't. We get an error saying that .getElementById expects zero type arguments.

We can see whether a function can receive type arguments by hovering over it. Let's try hovering .getElementById:

(method) Document.getElementById(elementId: string): HTMLElement | null

.getElementById contains no angle brackets (<>) in its hover. Let's contrasting it with a function that can receive type arguments, like document.querySelector:

(method) ParentNode.querySelector<Element>(selectors: string): Element | null

.querySelector has some angle brackets before the parentheses. It shows the default value inside them - in this case, Element.

So, to fix our code above we could replace .getElementById with .querySelector.

typescript
const audioElement = document.querySelector<HTMLAudioElement>("#player");

And everything works.

So, to tell whether a function can receive a type argument, hover it and check whether it has any angle brackets.

Matt's signature

This is a preview from my upcoming book.

If you’d like to receive updates about the book and all things TypeScript, subscribe below:

Share this Book Teaser with your friends

`any` Considered Harmful, Except For These Cases

Discover when it's appropriate to use TypeScript's any type despite its risks. Learn about legitimate cases where any is necessary.

Matt Pocock
Matt Pocock

No, TypeScript Types Don't Exist At Runtime

Learn why TypeScript's types don't exist at runtime. Discover how TypeScript compiles down to JavaScript and how it differs from other strongly-typed languages.

Matt Pocock
Matt Pocock

Deriving vs Decoupling: When NOT To Be A TypeScript Wizard

In this book teaser, we discuss deriving vs decoupling your types: when building relationships between your types or segregating them makes sense.

Matt Pocock
Matt Pocock

NoInfer: TypeScript 5.4's New Utility Type

Learn how TypeScript's new utility type, NoInfer, can improve inference behavior by controlling where types are inferred in generic functions.

Matt Pocock
Matt Pocock