1export function add(a: number, b: number) {2 return a + b;3}tsc--declaration$ node51export declare function add(a: number, b: number): number;1import { add } from "./math.js";2console.log(add("2", 3));console.log(add(2, 3));✗✓1export function add(a, b) {2 return a + b;3}math.tscodetypes"./math.js": numberapp.jsmath.js
Back: math.ts (the library module) and tscMiddle: math.d.ts (types only) and Node.js · Front: app.ts and math.js (code only)Tokens: files and types on their way between the cardsAt the back: math.ts, a module that exports add, and tsc. In the middle: math.d.ts, the declaration file, and Node.js. In front: app.ts, a module that imports add, and math.js. With --declaration, tsc writes math.js (the code, types erased) and math.d.ts (the types, no code). Checking app.ts needs only the .d.ts; running it needs only the .js. Every file shown is real: tsc 5.9 wrote math.js and math.d.ts exactly as shown, reported exactly that error, and node app.js prints 5.

Modules

  1. math.ts has export on line 1, so it is a module: a file with its own scope. Names inside it stay private unless exported. Here add is exported, so other files may use it.

Declaration files

Using a library

Run time

What happens

;; step 1/9 · module
math.ts
> 1  export function add(a: number, b: number) {
  2      return a + b;
  3  }

top-level export  →  this file is a module
add is exported  →  other modules can import it
anything not exported stays inside the file

Inside one project where math.ts is still present, tsc resolves ./math.js to math.ts itself (a .ts file is found before a .d.ts of the same name). The .d.ts file is for whoever gets only the compiled files, such as the users of an npm package. If a .js file has no .d.ts beside it, strict tsc reports error TS7016: Could not find a declaration file for module. Many npm packages ship their own .d.ts files; for others, the DefinitelyTyped project publishes them as @types packages.