Technology handbook
TypeScript 7 — Types, Compiler & Migration
The type system and the native TypeScript 7 compiler, with a practical path from modelling values to faster builds.
- Topics
- 31
- Sections
- 6
Compiler
What TypeScript 7 is
Move to the native compiler while keeping the familiar tsc workflow and type-checking model.
- The native compiler changes implementation rather than JavaScript runtime behavior.
- Validate diagnostics and emitted output against the previous toolchain.
- Check build and editor integration support before migration.
tsc and project references
Split large repositories into buildable projects and let the compiler reuse work safely.
- Declare project references to express build dependencies.
- Use build mode to check referenced projects in dependency order.
- Keep generated outputs and source boundaries explicit.
Compiler API compatibility
Identify tools that still need the TypeScript 6 API while the TypeScript 7 compiler matures.
- A native compiler is not a drop-in JavaScript Compiler API object.
- Inventory tools that import the typescript package at runtime.
- Keep a compatible toolchain for integrations awaiting native support.
Parallel builds
Tune TypeScript 7's native parallelism for faster checks in large repositories.
- Benchmark native compiler performance on representative repositories.
- Track memory and wall time under the available CPU budget.
- Check reproducibility before tuning parallel execution settings.
Build mode and incremental checks
Use project build mode and incremental state to shorten repeated checks in monorepos.
- Build mode coordinates referenced project outputs.
- Incremental metadata lets compatible builds reuse previous work.
- Invalidate or rebuild when configuration and dependencies change.
TypeScript language service
Understand editor diagnostics, navigation and tooling versions when adopting the native compiler.
- Align the editor's TypeScript tooling with the project version.
- Verify completion, navigation and refactoring during compiler upgrades.
- Keep editor integration compatibility separate from command-line success.
Type system
Unions and narrowing
Represent alternatives and let control flow refine a value to the safe branch.
- Use a union to represent several possible value shapes.
- Narrow with discriminants, typeof or runtime checks.
- Handle every branch before accessing variant-specific fields.
Generics
Preserve relationships between inputs and outputs instead of falling back to any.
- Introduce type parameters where inputs and outputs are related.
- Add constraints only for capabilities the implementation needs.
- Prefer inference over unnecessary explicit type arguments.
Intersection types
Compose capabilities from several object types while understanding conflicting properties.
- An intersection requires a value to satisfy every constituent.
- Conflicting property types can produce unusable never fields.
- Inspect the combined shape before composing public contracts.
Type guards
Teach the checker how runtime predicates establish a narrower static type.
- A type predicate connects a runtime check to static narrowing.
- Validate the complete condition claimed by the predicate.
- Unsound guards can make incorrect code appear type-safe.
Literal and primitive types
Model exact strings, numbers, booleans and object shapes instead of broad primitives.
- Use literal unions for a finite set of valid values.
- Distinguish wrapper objects from primitive string and number types.
- Preserve specific values where later narrowing depends on them.
Enums and alternatives
Choose unions or enums based on runtime needs, serialization and API ownership.
- String unions can model alternatives without generated enum objects.
- Enums provide named runtime values with their own emit behavior.
- Consider serialization and library boundaries before choosing.
Function overloads
Describe several supported call signatures while keeping one checked implementation.
- Expose overload signatures for genuinely different supported calls.
- Make the implementation compatible with every public signature.
- Prefer a union parameter when overloads add no useful relationship.
Variance and assignability
Understand when function parameters and generic containers can safely substitute for one another.
- Assignability depends on how a generic parameter is consumed or produced.
- Callback parameter safety differs from simple return-value compatibility.
- Test public generic APIs under strict function checking.
Type manipulation
Utility types
Transform existing types with Partial, Pick, Omit, Record, ReturnType and Awaited.
- Use Pick and Omit to select object properties.
- Use Partial or Required to change optionality deliberately.
- Derive function and promise results with ReturnType and Awaited.
satisfies and as const
Validate configuration while retaining precise literal types.
- satisfies checks compatibility without discarding useful inferred detail.
- as const preserves literal values and readonly tuple or object shapes.
- Neither construct freezes or validates incoming runtime data.
Template literal types
Generate string unions from naming rules and validate event or route keys.
- Combine literal unions to describe valid string patterns.
- Use key remapping to create related event or accessor names.
- Avoid enormous cross-product unions that slow checking.
Conditional and mapped types
Build reusable type transformations that inspect and remap the shape of another type.
- Conditional types choose results based on assignability.
- Mapped types transform keys and modifiers of existing shapes.
- Control distributivity when transforming a union as a whole.
Indexed access types
Derive property and array element types directly from an existing model.
- Read a property's type with Model['property'].
- Use an array's number index to obtain its element type.
- Constrain dynamic keys with keyof the source model.
Infer in conditional types
Extract related types inside reusable conditional transformations.
- Use infer to capture a matched type inside a conditional branch.
- Extract function parameters, return values or nested element types.
- Define what should happen when the source shape does not match.
Configuration
Strict compiler checks
Use strict mode and modern defaults to make invalid states visible at compile time.
- Enable strictness deliberately and fix meaningful diagnostics.
- Model null and undefined at actual data boundaries.
- Avoid suppressing errors through broad assertions or any.
Modules and resolution
Configure Node.js and bundler modules, including TypeScript 7's modern resolution rules.
- Match module and moduleResolution to the execution environment.
- Verify package exports and ESM/CommonJS interoperability.
- Remember compiler path mappings do not rewrite runtime imports.
tsconfig migration
Replace removed or deprecated options and make the compiler target explicit.
- Audit configuration for deprecated or removed options.
- Set target, module and type libraries for the actual runtime.
- Compare effective configuration before and after the upgrade.
noUncheckedIndexedAccess
Make indexed lookups acknowledge that a key may not exist at runtime.
- Indexed reads can include undefined when a key may be absent.
- Check lookup results before accessing their properties.
- Use explicit required properties where existence is guaranteed.
exactOptionalPropertyTypes
Distinguish an omitted optional property from one explicitly assigned undefined.
- An absent optional key differs from a present undefined value.
- Include undefined explicitly when assigning it is intentional.
- Test object spreading and patch payloads at API boundaries.
JavaScript interop
Decorators and classes
Type object-oriented code and standard decorators without weakening the public API.
- Distinguish standard decorators from legacy decorator semantics.
- Type constructor, instance and decorator context contracts explicitly.
- Verify required metadata integrations before switching modes.
Declaration files
Describe untyped JavaScript packages and publish accurate public types for consumers.
- Describe runtime exports accurately in .d.ts files.
- Keep ambient declarations separate from implementation code.
- Test published types against a small consuming project.
Module augmentation
Extend third-party declarations locally when a package's public types need an application-specific addition.
- Augment the exact module name exported by the package.
- Add compatible declarations rather than replacing implementation behavior.
- Ensure the augmentation file is included by the consumer's compiler.
JSDoc checking
Add useful static checking to JavaScript projects without converting every file to TypeScript.
- Enable checkJs for JavaScript that should receive diagnostics.
- Annotate parameters and reusable types with supported JSDoc tags.
- Migrate files gradually while retaining runnable JavaScript.
Migration
Migration from TypeScript 6
Clear deprecations first, then switch compiler versions and verify API-dependent tools.
- Resolve TypeScript 6 deprecations before adopting the native compiler.
- Verify dependencies that rely on compiler or language-service APIs.
- Compare CI diagnostics and emitted artifacts during the transition.
Migrating from any
Replace unchecked escape hatches with unknown, validation and precise domain types.
- Use unknown for data whose shape is not yet established.
- Validate at the boundary before assigning a domain type.
- Remove unsafe any flows incrementally with focused checking.