What is a “.d.ts” file in TypeScript?

.d.ts files are called type declaration files. They exist for one purpose only: to describe the shape of an existing module and they only contain type information used for type checking.

What is a “.d.ts” file in TypeScript?

TLDR

.d.ts files are called type declaration files. They exist for one purpose only: to describe the shape of an existing module and they only contain type information used for type checking.

Introduction

Upon learning the basics of TypeScript, you unlock superpowers.

At least that’s how I felt.

You automagically get warnings on potential errors, you get auto-completion out of the box in your code editor.

While seemingly magical, nothing with computers are.

So, what’s the trick here, TypeScript?

In a clearer language, how does TypeScript know so much? How does it decide what API is correct or not? What methods are available on a certain object or class, and which aren’t?

The answer is less magical.

TypeScript relies on types.

Occasionally, you do not write these types, but they exist.

They exist in files called declaration files.

These are files with a .d.ts ending.

A simple example

Consider the following TypeScript code:

// valid 
const amount = Math.ceil(14.99)

// error: Property 'ciil' does not exist on type 'Math'.(2339)
const otherAmount = Math.ciil(14.99)

See TypeScript playground.

The first line of code is perfectly valid, but the second, not quite.

And TypeScript is quick to spot the error: Property 'ciil' does not exist on type 'Math'.(2339)

The Typescript error spotting the wrong property access "ciil"
The Typescript error spotting the wrong property access "ciil"

How did TypeScript know ciil does not exist on the Math object?

The Math object isn’t a part of our implementation. It’s a standard built-in object.

So, how did TypeScript figure that out?

The answer is there are declaration files that describe these built-in objects.

Think of a declaration file as containing all type information relating to a certain module. It contains no actual implementation, just type information.

These files have a .d.ts ending.

Your implementation files will either have .ts or .js endings to represent TypeScript or javascript files.

These declaration files have no implementations. They only contain type information and have a .d.ts file ending.

Built-in Type Definitions

A great way to understand this in practice is to set up a brand-new TypeScript project and explore the type definition files for top-level objects like Math.

Let’s do this.

Create a new directory, and name it whatever’s appropriate.

I’ll call mine dts.

Change directories to this newly created folder:

cd dts

Now initialise a new project:

npm init --yes

Install TypeScript:

npm install TypeScript --save-dev
Installing TypeScript
Installing TypeScript

This directory should contain 2 files and one subdirectory

The files after installation
The files after installation

Open the folder in your favourite code editor.

If you investigate the TypeScript directory within node_modules, you’ll find a bunch of type declaration files out of the box.

Type declaration files in the typescript directory
Type declaration files in the typescript directory

These are present courtesy of installing TypeScript.

By default, TypeScript will include type definition for all DOM APIs, e.g., think window and document.

As you inspect these type declaration files, you’ll notice that the naming convention is straightforward.

It follows the pattern: lib.[something].d.ts.

Open up the lib.dom.d.ts declaration file to view all declarations related to the browser DOM API.

The dom declaration file
The dom declaration file

As you can see, this is quite a gigantic file.

But so are all the APIs available in the DOM.

Awesome!

Now, if you take a look at the lib.es5.d.ts file, you’ll see the declaration for the Math object, containing the ceil property.

The Math object in the declaration file
The Math object in the declaration file

Next time you think, wow, TypeScript is wonderful. Remember, a big part of that awesomeness is due to the lesser-known heroes: type declaration files.

It’s not magic. Just type declaration files.

External type definitions

What about APIs that aren’t built-in?

There’s a host of npm packages out there to do just about anything you want.

Is there a way for TypeScript to also understand the relevant type relationships for the said module?

Well, the answer is a resounding yes.

There are typically two ways a library author may do this.

(1) Bundled Types

In this case, the author of the library has already bundled the type declaration files as part of the package distribution.

You typically don’t need to do anything.

You just go ahead and install the library in your project, you import the required module from the library and see if TypeScript should automatically resolve the types for you.

Remember, this is not magic.

The library author has bundled the type declaration file in the package distribution.

(2) DefinitelyTyped (@types)

Imagine a central public repository that hosts declaration files for thousands of libraries?

Well, bring that image home.

This repository already exists.

The DefinitelyTyped repository is a centralised repository that stores the declaration files for thousands of libraries.

In all honestly, the vast majority of commonly used libraries have declaration files available on DefinitelyTyped.

These type definition files are automatically published to npm under the @types scope.

For example, if you wanted to install the types for the react npm package, you’d do this:

npm install --save-dev @types/react

If you find yourself using a module whose types TypeScript does not automatically resolve, attempt installing the types directly from DefinitelyTyped.

See if the types exist there. e.g.:

npm install --save-dev @types/your-library

Definition files added in this manner will be saved to node_modules/@types.

TypeScript will automatically find these. So, there’s no additional step for you to take.

Writing your declaration files

In the uncommon event that a library didn’t bundle its types and does not have a type definition file on DefinitelyTyped, you can write your own declaration files.

Writing declaration files in-depth is beyond the scope of this article, but a use case you’ll likely come across is silencing errors about a particular module without a declaration file.

Declaration files all have a .d.ts ending.

So to create yours, create a file with a .d.ts ending.

For example, assuming I have installed library untyped-module in my project.

untyped-module has no referenced type definition files, so TypeScript complains about this in my project.

To silence this warning, I may create a new untyped-module.d.ts file in my project with the following content:

declare module "some-untyped-module";

This will declare the module as type any.

We won’t get any TypeScript support for that module, but you’d have silenced the TypeScript warning.

Ideal next steps would include opening an issue in the module’s public repository to include a TypeScript declaration file, or writing out a decent one yourself.

Conclusion

Next time you think, wow, TypeScript is remarkable. Remember, a big part of that awesomeness is due to the lesser-known heroes: type declaration files.

Now you understand how they work!

Get a Free Typescript Book?

image-148
Build strongly typed Polymorphic React components 

Get this book for free