TypeScript 5.7 connects source imports to emitted JavaScript paths
Relative import rewriting lets source use TypeScript extensions during development and JavaScript extensions after emission. The transformation deliberately excludes package aliases and other paths whose runtime meaning belongs to a resolver.
Running TypeScript source directly creates a practical conflict for library authors. The runtime needs an import that names the existing .ts file, while a consumer of compiled output needs a path to emitted JavaScript. TypeScript 5.7, released November 22, adds relative import extension rewriting so one source tree can describe both stages without manually changing every local specifier.
The option is rewriteRelativeImportExtensions. It applies to relative paths beginning with ./ or ../ and ending in supported TypeScript extensions. The compiler converts them to the corresponding JavaScript extension during emission. This is a deliberately limited transformation, which matters because module specifiers can also represent package identities, aliases, or values assembled at runtime.
I find this useful because TypeScript's ecosystem supports several ways to execute source before publication. A library should not need an unrelated source convention merely to make its development runtime happy. The improvement is strongest where the relationship is concrete, with a local source file and the JavaScript file emitted from it. More general resolution still belongs to the system responsible for interpreting the path.
A source extension can be correct during development
Node's initial type-stripping documentation illustrates the requirement clearly. Direct execution uses explicit source extensions, such as ./file.ts. That runtime does not consult tsconfig.json to reconstruct a compiler transformation. The written specifier therefore names the source file the runtime must actually load.
TypeScript has already supported checking these references through allowImportingTsExtensions. Checking the source is only part of the problem, however. A package that emits JavaScript needs those references to name the emitted files. Accepting the source path for checking does not make a .ts file appear in the JavaScript distribution, nor make its extension correct for an output that contains only .js files.
The new option supplies the missing connection for ordinary local imports. This is a relationship between two representations of the same module graph. Development uses the source graph, while publication uses the emitted graph. A transformation can preserve the graph's local relationships when the extension correspondence is known.
That does not require source execution to become the publication format. A project can continue distributing JavaScript and declarations. The author can use direct TypeScript execution locally and still produce conventional output for consumers. The feature helps keep those choices separate instead of forcing every consumer to adopt the author's development loader.
The compiler rewrites a specific class of paths
The release announcement identifies .ts, .tsx, .mts, and .cts paths in non-declaration source. Their emitted counterparts use the applicable JavaScript extensions. An illustrative ES module pair can avoid ambiguity by using .mts. In message.mts, the source exports an ordinary typed function:
export function message(name: string): string {
return "Hello, " + name;
}
The consuming main.mts refers to the source extension. With relative rewriting enabled during JavaScript emission, the corresponding output names the emitted module instead. The source and output references differ only in the part the compiler can derive from the module's representation:
import { message } from "./message.mts";
console.log(message("reader"));
The emitted main.mjs imports ./message.mjs. Function type annotations disappear through ordinary TypeScript emission. The path rewrite is additional work that preserves which neighboring module the runtime should load. It does not change the application function or select a different package implementation.
A configuration can express the relevant module and rewrite choices directly. This illustration assumes the files are part of the project's selected inputs and that the output directory is reserved for emitted files:
{
"compilerOptions": {
"module": "nodenext",
"target": "es2022",
"rewriteRelativeImportExtensions": true,
"outDir": "./dist"
}
}
The option does not add an extension to extensionless imports or alter a path already ending in .js. Those references may already describe the author's intended runtime convention. Restricting the transformation avoids treating every specifier as an incomplete source filename needing repair. The compiler only changes the supported relative source-extension pattern.
Package identities are outside the local rewrite
An import such as some-package/file.ts does not begin with a relative prefix, so it remains unchanged. The same applies to scoped packages and package subpath imports beginning with #. These strings can have meanings established by package exports or imports maps. Replacing an extension without resolving that meaning would not establish which output the package author intends.
TypeScript paths mappings and baseUrl are also outside this transformation. A checker may resolve an alias to source while the runtime requires a separate alias implementation. Rewriting the trailing extension would not create that implementation. The release makes this explicit because an alias can superficially resemble a filename while still depending on configuration external to the import statement.
For a multi-package workspace, the announcement suggests conditional exports with scoped development conditions. A package can expose source under its own development condition and compiled JavaScript under its normal import condition. The scope matters because a generic condition named development can accidentally activate the same convention in dependencies that did not expect to be executed as TypeScript.
This is a different solution from rewriting a neighboring file's extension. A package export map chooses an implementation according to a condition understood by the resolver. Relative rewriting changes a literal path whose correspondence the compiler knows. Both can support source-based development, but they operate at different naming boundaries and should be described separately.
Runtime-generated paths cannot be treated like fixed imports
The announcement also explains why dynamic imports complicate general path rewriting. An import argument can come from a function choosing between multiple values. It can be supplied by a dependency whose implementation is outside the emitted source. The compiler cannot redefine every such value's meaning by rewriting a fixed set of import statements.
A string can also be used for purposes other than module loading before it reaches an import. Its extension alone does not establish whether the program intends a source or distribution path. A safe account of the feature therefore starts with the statically recognizable relative case. It should not promise that arbitrary dynamic naming conventions become portable after enabling one option.
This limit is useful for library authors because it identifies what the new configuration establishes. Local imports with supported source extensions can become local imports with output extensions. Package references and runtime-computed paths retain their own resolution requirements. The resulting distribution still needs those requirements to match what its consumer's runtime understands.
Project ownership receives a separate resolution improvement
TypeScript 5.7 also changes editor project discovery. The nearest tsconfig.json may not own a file, particularly when source and tests use separate configurations referenced by a root solution. The service now continues searching ancestors for an appropriate configuration. This helps it apply the intended settings instead of treating proximity alone as ownership.
The release also makes probing composite projects cheaper. Such projects must declare all input source files up front. The service can therefore inspect that root set when deciding whether a file belongs, avoiding a full traversal of every referenced project's source. This addresses the case where opening an unrelated script caused a large repository to load unexpectedly.
These changes concern editor configuration rather than emitted import paths. Their common practical relevance is that a path has a meaning within a particular project and runtime arrangement. Correct checking needs the right configuration. Correct execution needs the right module reference. Similar filenames alone do not establish either relationship.
JSON imports under nodenext also receive runtime-oriented validation. An ES module import needs the type: "json" attribute, and JSON data is accessed through the default export. These checks align the accepted source form with the module operation the runtime expects.
Library declarations preserve additional buffer information
The release adds the ES2024 target and library declarations. Its typed arrays become generic over their underlying buffer type because ArrayBuffer and SharedArrayBuffer now differ in relevant declarations. A default type argument preserves familiar uses of names such as Uint8Array, while more specific signatures can retain which buffer kind backs a view.
That is another example of a compatibility improvement requiring a precise description. The generic parameter carries information rather than changing the runtime array object. Related Node declaration packages may need updating when their older assumptions conflict with the new signatures. A compiler upgrade can expose that mismatch even though the application's JavaScript operations remain the same.
I regard relative import rewriting as the release's clearest improvement to publication workflows. It allows local development source to name the files that actually exist, then derives the corresponding emitted references. The restrictions on package aliases and dynamic paths keep that promise specific. A library can use the feature knowing which local relationships it preserves and which resolver conventions remain its own responsibility.