"One file per module. Import what you need; export what others need."
The ESM model
In ES Modules, each file is its own module with its own scope. Variables declared at the top level are private to the file unless you export them. To use them in another file, you import. There's no global namespace; everything is scoped.
This is the standard JavaScript module system. TypeScript adopts it directly — no special syntax, no transpilation hacks (when targeting modern environments). The same import/export you write in TypeScript runs natively in modern Node.js, Bun, Deno, and browsers.
Named exports vs default exports
Named: export function greet() { ... }; import with import { greet } from './mod'. The name is fixed at the export site; you can alias on import with as.
Default: export default class Foo { ... }; import with import Foo from './mod'. The importer chooses the name. One default per module.
Modern style guides lean toward named exports — they keep names consistent across imports, support better tree-shaking with bundlers, and make refactoring with rename-symbol IDE actions reliable. Default exports remain in some libraries (React itself, for example) for historical reasons.
Re-exports
export { greet } from './mod' re-exports from another module without importing locally. Common in index.ts barrel files that consolidate a directory's public API.