The compiler and runtime

Use declaration files

Understand how .d.ts files describe JavaScript APIs and where library types come from.

Types are erased, so a compiled library ships no types of its own. A declaration file puts them back. It is a .d.ts file that contains type information and no implementation. It tells TypeScript how a piece of JavaScript may be called, without providing the JavaScript itself.

Say a package exports a slugify() function written in plain JavaScript. A declaration can describe it in one line:

declare function slugify(value: string): string
export { slugify }

The declare keyword says “this exists somewhere, trust me about its shape”. With that file in place, TypeScript checks every call as if the function had been written in TypeScript. Pass a number and you get:

Argument of type 'number' is not assignable to parameter of type 'string'.

At runtime, the .d.ts file does nothing. The real slugify() still comes from the JavaScript package.

Where library types come from

Most packages you install today ship their own declarations. Look for a types field in the package’s package.json, or .d.ts files next to the JavaScript. If they are there, you are done.

Older JavaScript libraries often have community-maintained declarations published under the @types scope:

npm install --save-dev @types/express

Do not install @types packages out of habit. If the library already includes types, the extra package is redundant and can even conflict with the real ones.

When a package has no types

Import a package with no declarations at all, and strict mode refuses:

Could not find a declaration file for module 'slugify-lite'. '/node_modules/slugify-lite/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/slugify-lite` if it exists or add a new declaration (.d.ts) file containing `declare module 'slugify-lite';`

The message lists your two options. Install the @types package if one exists. Otherwise write a small .d.ts file yourself, starting with the one-line declare module version and adding real signatures for the functions you use.

Declarations are promises

A declaration file is a contract, and nothing checks that it matches the implementation. If the .d.ts says slugify() returns a string and the JavaScript actually returns null on empty input, TypeScript approves code that crashes. When a well-typed call fails at runtime in a surprising way, read the library’s actual source before you blame your code. The declaration is the side that can be wrong.

Try this: write a declaration claiming a function returns number, then make the JavaScript return a string. Notice which side TypeScript trusts, and what happens when you run it.

Lesson completed