Skip to Content
Course content

114: Writing Your Own .d.ts for an Untyped Library

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I joined a project that relied on a niche JavaScript library for handling complex geospatial calculations. It was a brilliant piece of engineering, but it was written in 2012 and had absolutely no type definitions. For the first two weeks, I felt like I was coding with a blindfold on. Every time I called a function, TypeScript just gave me an any, and I found myself constantly jumping back and forth between my code and the original library's source files just to check if a parameter was supposed to be an array of numbers or a single coordinate object. I spent more time reading the implementation than actually writing features.

When you hit this wall, you have two choices: stay in any land or build your own declaration file. Since the community hasn't provided a @types package for every single library on NPM, knowing how to write your own .d.ts file is a superpower. It allows you to "teach" TypeScript how a JavaScript library works without actually touching the library's source code.

Defining the Module Boundary

To start, you need to tell TypeScript that this library exists as a module. We do this using the declare module syntax. Imagine we're dealing with a hypothetical library called fancy-canvas-utils that provides some helper functions for drawing. You'd create a file—usually named fancy-canvas-utils.d.ts—and wrap everything in a module declaration.

declare module 'fancy-canvas-utils' {
    export function drawCircle(x: number, y: number, radius: number): void;
    export function clearCanvas(): void;
}

At this point, the any warnings vanish. You've essentially created a "contract" that tells the compiler, "Trust me, when I import from this package, these functions exist and they take these specific types." I usually start by mapping out the most common functions first, rather than trying to document the entire library in one sitting. There's no rule saying your declaration file has to be 100% complete on day one.

Tightening the Type Definitions

Once you have the basic functions mapped, you'll likely find that the library uses complex objects. Using any inside your .d.ts file defeats the purpose of doing this in the first place. Instead, define interfaces that represent the library's internal data structures. Let's say fancy-canvas-utils has a CanvasConfig object that controls the render quality.

declare module 'fancy-canvas-utils' {
    export interface CanvasConfig {
        antialias: boolean;
        resolution: number;
        backgroundColor?: string; // Optional property
    }

    export function initCanvas(config: CanvasConfig): void;
    export function drawCircle(x: number, y: number, radius: number): void;
    export function clearCanvas(): void;
}

I've found that adding optional properties (using the ?) is critical here. Many JS libraries have "options" objects where only one or two keys are actually required. If you make them all required in your type definition, you'll end up fighting the compiler every time you try to use the library with a simple configuration. Keep it flexible, but accurate.

One last tip: place these files in a folder like @types at the root of your project and make sure your tsconfig.json is configured to look there via the typeRoots option. This keeps your custom definitions separate from your actual application logic, making the project much cleaner for the next developer who comes along.




📋 Practical Task

Creating Type Definitions for the "Easy-Auth-JS" Library

You are using a small, untyped legacy library called easy-auth-js. The library has the following JavaScript implementation (which you cannot change):

// easy-auth-js.js
export function login(username, password) { /* ... */ }
export function logout() { /* ... */ }
export function setUserSession(sessionData) { /* ... */ }

The sessionData object is expected to have a token (string) and an optional expiry (number).

Your Task: Create a declaration file easy-auth-js.d.ts that provides full type safety for this library. You must:

  • Use declare module 'easy-auth-js'.
  • Define the login and logout functions with appropriate types.
  • Create an interface for SessionData and use it as the parameter type for setUserSession.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.