This episode covers using external libraries safely: @types packages from DefinitelyTyped, the structure of .d.ts declaration files, ambient declarations with declare, and module resolution settings in tsconfig so imports resolve correctly.

A JavaScript library without types becomes a blind spot. When you import it, TypeScript complains because it can't find a declaration. Episode 14 covers how to close that gap: installing type packages, writing your own declarations, and making sure the compiler finds all of them.
Most popular libraries provide types through DefinitelyTyped, the @types package repository. Other libraries already ship built-in types. The rest need ambient declarations you can write yourself.
Episode 14 dissects the declaration ecosystem: the role of @types, the contents of .d.ts files, the declare keyword for environments without an implementation, and the module resolution options that connect everything.
Many pure JavaScript libraries have their types maintained by the community in @types packages. Installing them is simple:
bun add -d @types/node @types/expressThe command above installs type declarations for Node.js and Express as devDependencies. The naming rule is clear: types for express live in @types/express, types for node in @types/node. Once installed, TypeScript recognizes those declarations automatically without any extra import.
If a library already ships built-in types, no @types package is needed:
npm view zod typesThe npm view zod types command reads the types field in the package metadata. If it has a value, the library includes its own declarations and you only need to install the library itself. Installing a duplicate @types for a typed library can cause conflicts.
Declaration files end in .d.ts and contain only types, no implementation:
export interface OpsiKoneksi {
host: string;
port: number;
}
export interface OpsiAuth {
token: string;
}
export function konek(opts: OpsiKoneksi): Promise<void>;The file koneksi.d.ts describes the shape of a module without runtime code. The compiler uses it for checking, then discards it at build time because it produces no JavaScript. This is the format used by @types packages and libraries' built-in declarations.
Besides module declarations, there are also global declarations. A file like global.d.ts describes variables available without an import, for example declare const for an object injected by the server. This pattern is often used to integrate script tags and global page data.
Info
Declaration file names generally follow the package name: types for lib-lama live in lib-lama.d.ts or in types/lib-lama/index.d.ts. This convention makes finding declarations predictable for both the compiler and other developers.
A library with no types at all can be declared with the declare keyword:
declare module "lib-lama" {
export function inisialisasi(kunci: string): void;
export const versi: string;
}The declare module "lib-lama" block tells the compiler the module's shape without needing an implementation file. When your code imports lib-lama, TypeScript uses this declaration. For modules too large to fully declare, a loose declaration is a shortcut:
declare module "lib-lama-2" {
const apaAja: any;
export = apaAja;
}The declaration export = apaAja with any marks the whole module as untyped. It's fast, but temporary. Once you understand the real shape, update the declaration to precise types. Files with ambient declarations like this are stored in a types folder and registered with the compiler.
For imports and declarations to be found, module resolution must be configured correctly:
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"typeRoots": ["./node_modules/@types", "./types"]
}
}The moduleResolution option determines how files are searched, for example NodeNext for Node.js. The typeRoots option adds a local types folder as an additional declaration source. With this setup, both your own declarations and @types packages are recognized by the compiler.
Warning
Replace any with precise types as soon as possible. A loose declaration disables checking for the entire module, exactly like the any variable discussed in episode 3.
Episode 14 makes you unafraid of untyped libraries. With @types packages, .d.ts files, ambient declarations, and proper module resolution, every dependency can be used with clear type guarantees.
Key takeaways:
@types packages provide community types through DefinitelyTyped.@types package.declare module declares a module that has no types.any are only a temporary bridge.moduleResolution and typeRoots control how types are found.In the next episode 15 we'll discuss JSX/TSX and modern frontend work — typing React components, props, event handlers, and generics on components.