Skip to Content
Course content

48: Declaration Files (.d.ts)

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

A few years ago, I was tasked with migrating a massive legacy project to TypeScript. The codebase relied on a very specific, niche JavaScript library for handling complex date calculations that hadn't been updated since 2014. The moment I changed my file extensions to .ts, my editor turned into a sea of red squiggles. TypeScript had no idea what this library was, what functions it exported, or what arguments those functions took. I found myself constantly using //@ts-ignore just to get the project to compile, which felt like cheating—and it was. I was effectively writing JavaScript in a TypeScript file, losing all the safety we'd worked so hard to implement.

That's when I really dove into declaration files. A .d.ts file is essentially a "map" for your code. It doesn't contain any actual logic—no function bodies, no variable assignments—just the types. It tells the TypeScript compiler: "I know this code exists in JavaScript at runtime, and here is exactly what the shapes of those objects and functions look like."

Mapping JavaScript to TypeScript

When you install a popular library like React or Lodash, you'll often see a separate package like @types/react. Those are declaration files maintained by the community via DefinitelyTyped. But when you're dealing with a private internal library or an obscure package, you have to write these yourself.

If you have a JavaScript file called utils.js that exports a function to format currency, TypeScript sees it as any. To fix this, you create a utils.d.ts file in the same directory. You aren't rewriting the logic; you're just describing the interface. It looks like this:

// utils.d.ts
export declare function formatCurrency(amount: number, currencyCode: string): string;
export declare const DEFAULT_LOCALE: string;

Notice the declare keyword. This is the secret sauce. It tells TypeScript that the implementation is defined elsewhere (in the JS file) and it should just trust the type definition provided here. Once this file is in place, the red squiggles vanish, and you get full IntelliSense for formatCurrency across your entire project.

Ambient Declarations and the Global Scope

Sometimes you aren't dealing with a module you import, but rather something that just "exists" in the environment—like a global variable injected by a <script> tag in your HTML or a Node.js global. If you try to use a global variable like window.myCustomAnalytics, TypeScript will complain that the property doesn't exist on the Window type.

To solve this, we use "ambient declarations." By using declare global or simply declaring a variable outside of a module (a file without import or export statements), you can augment the global scope. I've found this incredibly useful when integrating third-party tracking pixels or legacy CMS variables.

// globals.d.ts
declare global {
  interface Window {
    myCustomAnalytics: {
      trackEvent: (name: string, data: object) => void;
      userId: string;
    };
  }
}

export {}; // This ensures the file is treated as a module

I added the export {} at the bottom because, in TypeScript, a file without an import or export is treated as a script that defines things in the global scope. Adding the empty export forces it to be a module, which is often necessary when you're specifically using declare global to avoid naming collisions and ensure the compiler handles the augmentation correctly.




📋 Practical Task

Creating Type Definitions for the "SuperChart" Legacy Library

You are working with a legacy JavaScript library called super-chart.js. It is not written in TypeScript and has no @types package. The library provides a global object called SuperChart that is attached to the window object.

The library has the following functionality:

  • A method render(elementId: string, data: number[]) that returns a boolean indicating if the render was successful.
  • A method setTheme(theme: 'light' | 'dark') that returns nothing.
  • A property version which is a string.

Your Goal: Create a declaration file named superchart.d.ts that uses declare global to augment the Window interface. Ensure that window.SuperChart is fully typed so that you can call its methods with autocomplete and type safety in your main TypeScript files.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.