Modules and declaration files
A TypeScript file with a top-level import or export is a module, and modules share code by exporting and importing names. Follow one small library, math.ts, as tsc --declaration splits it into two files: math.js, the code with the types removed, and math.d.ts, the types with the code removed. Then watch tsc check another module against the .d.ts file alone, catch a bad call, and Node.js run the result without ever loading the types.
5 quick questions
Pick an answer to see why. Your score saves with your progress.
01What makes a TypeScript file a module?
Any file with a top-level import or export is a module, with its own scope. A file with neither is a script, and its top-level names are global.
02What is inside math.d.ts after tsc --declaration math.ts?
A declaration file holds types and no code. declare says the function exists and its code lives elsewhere, in math.js. tsc even wrote the return type, number, which it inferred.
03A library ships only math.js and math.d.ts. Your app.ts calls add("2", 3). What happens when you run tsc?
tsc follows ./math.js, finds math.d.ts beside it and checks the call against it: app.ts(2,17): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
04When Node.js runs app.js, which files does it load?
The import "./math.js" names the JavaScript file, so Node.js loads math.js. Types, including whole .d.ts files, are gone at run time; the program prints 5.
05Why does app.ts import "./math.js" and not "./math.ts"?
Imports are written for run time, when only .js files exist. With --module nodenext, tsc looks behind ./math.js for math.ts or math.d.ts to find the types.
Glossary
- Module
- A file with a top-level import or export. Its names are private unless it exports them.
- export
- Makes a name in a module available to other modules.
- import
- Brings an exported name from another module into this one.
- Declaration file (.d.ts)
- A file that holds only types: what exists and its shape, with no code to run.
- declare
- A keyword that says something exists and its code lives elsewhere, so only its type is written.
- --declaration
- The tsc option that writes a .d.ts file next to each .js file it outputs.
- Module resolution
- How tsc finds the file an import path refers to, for example math.d.ts behind "./math.js".
- @types package
- An npm package from the DefinitelyTyped project that holds .d.ts files for a JavaScript library that ships none.
Notes and sources
- A file with no top-level import or export is a script, not a module: its top-level names are global and shared with every other script. Adding export {} makes such a file a module.
- The commands assume a package.json with "type": "module", so Node.js treats .js files as ES modules; with --module nodenext, TypeScript then wants the .js extension in import paths, the name of the file that will run.
- 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.
- 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.
- 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.
- TypeScript Handbook: Modules
- TypeScript Handbook: Modules - Theory
- TypeScript Handbook: Modules - Reference (module resolution)
- TypeScript Handbook: Declaration Files - Introduction
- TypeScript Handbook: Declaration Reference (declare by example)
- TypeScript Handbook: Publishing declaration files
- TypeScript Handbook: Consumption (@types packages)
- TSConfig Reference: declaration
- Node.js documentation: package.json "type"
- MDN: JavaScript modules
Quiz Unlocks after the last step.