From b32bf7f28dc07e1b08627d2a52adc17e414058d7 Mon Sep 17 00:00:00 2001 From: Changhyun Kim Date: Wed, 22 Jul 2026 10:53:47 +0900 Subject: [PATCH] docs(declaration-files): document implicit export visibility in .d.ts modules All top-level declarations in a module .d.ts file (types and values) are importable from outside even without the export keyword, and appending 'export {}' opts back into only-explicit-exports visibility. This behavior is by design but was not covered in the handbook. Refs microsoft/TypeScript#32182 --- .../copy/en/declaration-files/Deep Dive.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/packages/documentation/copy/en/declaration-files/Deep Dive.md b/packages/documentation/copy/en/declaration-files/Deep Dive.md index 89950e967a42..0455b11501ef 100644 --- a/packages/documentation/copy/en/declaration-files/Deep Dive.md +++ b/packages/documentation/copy/en/declaration-files/Deep Dive.md @@ -230,3 +230,50 @@ The second block creates the following name meanings: - A type `X` + +## Export Visibility in Declaration Files + +Module declaration files (`.d.ts` files with a top-level `import` or `export`) +have a behavior that often surprises people: every top-level declaration is +reachable from outside the module, whether or not it is marked `export`. + +Say we have a module file `foo.d.ts`: + +```ts +interface Options { + count: number; +} +export declare function make(options: Options): void; +``` + +Then consumed it: + +```ts +import { make, Options } from "./foo"; +``` + +In a normal `.ts` module, importing `Options` would be an error +("Module './foo' declares 'Options' locally, but it is not exported."), +but in a declaration file the non-exported name is still importable. + +This applies to values as well as types: a `declare const` or `declare class` +without `export` can also be named in an import. +Be careful with values, though — if the underlying JavaScript module doesn't +actually export that binding, the import will fail at load time under ES +modules, or produce `undefined` under CommonJS interop, even though it +type-checks. + +To opt out of this behavior and make a declaration file behave like a normal +module — where only explicit `export`s are visible from outside — add an empty +export to the file: + +```ts +interface Options { + count: number; +} +export declare function make(options: Options): void; +export {}; +``` + +With the `export {}` present, importing `Options` elsewhere is now an error, +even though the file was already a module before adding it.