Writing a TypeScript Type Definition for a JavaScript npm Package
Tags: javascript, typescript
Published: 2020-12-14 12:51:01

---
title: Writing a TypeScript Type Definition for a JavaScript npm Package
published: true
description: I'm building a social media network and collaboration tool based on mind maps, documenting my work in this series of blog posts
tags: javascript,typescript
#cover_image: https://dev-to-uploads.s3.amazonaws.com/i/dybvdymmru80qxd8cgc4.gif
series: Nuffshell Coding Diary
---

*I'm building a social media network and collaboration tool based on mind maps, documenting my work in this series of blog posts. Follow me if you're interested in what I've learned along the way about building web apps with React, Tailwind CSS, Firebase, Apollo/GraphQL, three.js and TypeScript.*

# Rant Time

My honeymoon with TypeScript is over. I've spent much more time than I would care to admit to make it work properly with my 3D mind map demo, and I'm starting to wonder if it is really worth it.

What especially threw me off was creating a type definition file (*.d.ts*) for npm packages that don't have types.

The [official documentation](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html) on this is quite lengthy, but in my opinion fails to explain the most basic things in a plain, easy to understand way.

Unfortunately, there are surprisingly few blog articles out there on the subject, and those few are mostly outdated.

OK, got that off my chest, let's move on…

# Today's Goal

I'm using this npm package in my project:

* [THREE.Interactive](https://github.com/markuslerner/THREE.Interactive)

This is a JavaScript only library, it does not provide any TypeScript type definitions, so I'll create my own.

# Type Definition Files

My project is based on [create-react-app](https://create-react-app.dev). When you create a React app with `--template typescript`, you get everything set up for you to start using TypeScript right away.

Among other things, CRA creates a file *react-app-env.d.ts* in the source directory, with this content:

```typescript
/// <reference types="react-scripts" />
```

The weird `reference` statement includes a bunch of pre-defined types for the project to use. For example, this makes sure that you can import styles from CSS modules in TypeScript modules without the compiler complaining.

Files ending with `.d.ts` are called declaration files. For adding types for the *THREE.Interactive* library, I can add type declarations to the *react-app-env.d.ts* file, or I can create an additional file next to it, for example, *three.interactive.d.ts*. 

All these declaration files are picked up by the TypeScript compiler automatically, I don't have to configure any paths for them to be included or anything like that.

# Analyzing My Code

To see which types I have to declare, I take a look at the locations in my code where I'm using the library.

Here are the relevant lines, I've left out the code that doesn't have anything to do with *THREE.Interactive*:

*initializeScene.ts*

```typescript
import { InteractionManager } from "three.interactive";

const interactionManager = new InteractionManager(renderer, camera, canvas);
```

*RenderCache.ts*

```typescript
import { InteractionManager } from 'three.interactive';

interface Constructor {
  interactionManager: InteractionManager;
}

export default class RenderCache {
  private interactionManager: InteractionManager;

  constructor({ interactionManager }: Constructor) {
    this.interactionManager = interactionManager;
  }

  preRender(data: MindMapData) {
    return Promise.all(
      data.nodes.map(async ({ name, val, id }) => {
        const sprite = await renderToSprite(
          <MindMapNode label={name} level={val} />
        );
        this.interactionManager.add(sprite);
      })
    );
  }
}
```

*renderMindMap.ts*

```typescript
interactionManager.update();
```

So this means I have to add a type declaration for a class `InteractionManager`, which I'm instantiating in *initializeScene.ts*.

I have to declare two methods:

* `add`, which I'm using in *RenderCache.ts*
* `update`, which I'm using in *renderMindMap.ts*

The library actually does more than this, but I decide to only declare types for the stuff I'm actually using.

# Adding the Declarations

I'm adding the type declarations to *react-app-env.d.ts* so that I end up with this:

```typescript
/// <reference types="react-scripts" />

declare module "three.interactive" {
  export class InteractionManager {
    constructor(
      renderer: THREE.Renderer,
      camera: THREE.Camera,
      canvas: HTMLCanvasElement
    );

    update(): void;

    add(object: THREE.Sprite): void;
  }
}
```

# WTF?

This works, the TypeScript compiler now makes sure I don't pass any illegal arguments to the constructor of `InteractionManager` or its `update` or `add` methods.

You may have noticed that my type declaration references types from the *three.js* library (`THREE.Renderer`, `THREE.Camera` and `THREE.Sprite`).

I thought I would have to import these types from *three.js* to make my type declaration work. Adding this to my *react-app-env.d.ts* seemed logical to me:

```typescript
import * as THREE from 'three';
```

When I did this, however, the compiler gave me this error:

> Could not find a declaration file for module 'three.interactive'. '/node_modules/three.interactive/build/three.interactive.js' implicitly has an 'any' type.
  Try `npm install @types/three.interactive` if it exists or add a new declaration (.d.ts) file containing `declare module 'three.interactive'

That's right – the compiler didn't tell me that there was something wrong my my import, it just ignored the *.d.ts* with the import altogether.

`THREE` for some reason is automagically already available, I guess as a global type, in my type declaration.

# Check It Out

I don't have a CodeSandbox for you to try out this article's code, because CodeSandbox seems to not work properly with TypeScript, so I ditched it after a lot of frustrating trial and error.

Here's a repository on GitHub instead:

{% github nuffshell/nuffshell-blog-11-example %}

# To Be Continued…

I'm planning to turn my mind map into a social media network and collaboration tool and will continue to blog about my progress in follow-up articles. Stay tuned!