TYPESCRIPT / CHAPTER 10

Module boundaries and type-only dependencies

Intermediate

Separate 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.

TypeScript example
// 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}])
Result
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

Chapter checkpoint

What does import type {Ticket} from "./model" provide?