TYPESCRIPT / CHAPTER 10
Module boundaries and type-only dependencies
IntermediateSeparate compile-time contracts from runtime code.
By the end of this chapter
- Export reusable types and values
- Use type-only imports when appropriate
- Keep external input validation at module boundaries
The idea
A module can export both runtime values and compile-time types. import type explicitly marks a dependency needed only for checking and removed from emitted JavaScript. This helps readers distinguish a shape contract from a value whose module must exist at runtime. Actual module resolution and supported file formats still depend on the compiler configuration and execution host.
A type-only import cannot be used to construct a runtime value. Conversely, a class may be needed both as a type and as a constructor, so import it normally when new is required. Keep modules’ initialization effects predictable, and do not assume importing a type loads a validator. The example shows separate files to explain the boundary; it requires an appropriate module toolchain rather than a single-file browser execution claim.
// model.ts
export type Ticket = {id: string; done: boolean};
// report.ts
import type {Ticket} from "./model";
export function countDone(items: readonly Ticket[]): number {
return items.filter(item => item.done).length;
}
// Example call after importing countDone:
// countDone([{id: "a", done: true}, {id: "b", done: false}])The example call returns 1.
Ticket guides checking without being a runtime object imported by the generated program.
Try it yourself
Separate a record type, a runtime parser, and a reporting function into modules with explicit imports.
Use your own editor for this exercise. Runnable challenges on CodeQuest are available for JavaScript, Python, and SQL.
CHECK YOUR UNDERSTANDING