Skip to Content
Course content

128: Handling Third-Party Libraries Without Types

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

When you first encounter a JavaScript library that doesn't have a corresponding @types package, it's easy to feel like you've hit a brick wall. I see this all the time with developers moving from JS to TS.

The "I'm forced to use any" Trap

The common misconception is that if a library isn't in DefinitelyTyped (the @types repository), your only choice is to use any or a // @ts-ignore comment to make the compiler shut up. You might think, "Well, the types don't exist, so I guess I'm just writing JavaScript in a .ts file now."

Let's look at why that's a mistake. Imagine you're using a small, niche library called canvas-confetti to add some celebration effects to your app. You install it, try to import it, and boom: "Could not find a declaration file for module 'canvas-confetti'."

import confetti from 'canvas-confetti';

// The compiler is screaming at me here
confetti({
  particleCount: 100,
  spread: 70
});

If you just cast it to any, you lose everything that makes TypeScript valuable. You won't get autocomplete for the options object, and if the library author changes particleCount to count in the next version, your code will compile perfectly and then crash in production. You've essentially opted out of the safety net right where you need it most: at the boundary between your code and an external dependency.

Surgical Type Declarations

Here is the secret: you don't need to type the entire library. You only need to type the parts you actually use. I call this "surgical typing." Instead of waiting for a stranger on the internet to contribute to DefinitelyTyped, you can create your own ambient declaration file.

First, create a folder—usually @types or types—in your project root. Inside, create a file named canvas-confetti.d.ts. The .d.ts extension tells TypeScript this is a declaration file, not implementation code.

// types/canvas-confetti.d.ts
declare module 'canvas-confetti' {
  export interface ConfettiOptions {
    particleCount?: number;
    spread?: number;
    origin?: { x: number; y: number };
  }

  function confetti(options?: ConfettiOptions): void;
  export default confetti;
}

Now, you need to make sure TypeScript actually looks in that folder. In your tsconfig.json, add your types folder to the typeRoots array:

{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./types"]
  }
}

By doing this, you've told the compiler: "I know this module doesn't have official types, but trust me, it looks like this." Suddenly, your IDE provides autocomplete, and you have type safety for that specific library. If you find yourself using a new function from that library later, you just add one line to your .d.ts file. It's a tiny investment of time that prevents a whole category of runtime bugs.

I've spent years maintaining large codebases, and the most fragile parts are almost always the "any" holes left behind because someone didn't want to spend five minutes writing a declaration file. Don't be that developer.




📋 Practical Task

Typing the Legacy-Chart-JS Utility

You've inherited a project that uses a legacy internal JavaScript library called legacy-chart-utils. It has no type definitions, and your team is tired of seeing any everywhere in the dashboard code.

Your goal: Create a declaration file that provides type safety for the renderChart function used in the project.

The renderChart function takes two arguments:

  1. An elementId (a string).
  2. A config object containing:
    • data: An array of numbers.
    • color: A string.
    • showLegend: A boolean.

Requirements:

  • Create the declare module 'legacy-chart-utils' block.
  • Define an interface for the config object.
  • Ensure the function is exported as the default export of the module.
// Write your .d.ts content here:
Rating
0 0

There are no comments for now.

to be the first to leave a comment.