Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Proposal: deprecateimportsNotUsedAsValues andpreserveValueImports in favor of single flag #51479

Closed
Assignees
andrewbranch
Labels
CommittedThe team has roadmapped this issueFix AvailableA PR has been opened for this issueSuggestionAn idea for TypeScript
@andrewbranch

Description

@andrewbranch

Background:importsNotUsedAsValues

importsNotUsedAsValues was introduced alongside type-only imports in#35200 as a way to control import elision. In particular, Angular users often experienced runtime errors due to the unintended import elision in files like:

import{MyService}from'./MyService';classMyComponent{constructor(privatemyService:MyService){}}

It appears to TypeScript as if the import declaration can be elided from the JS emit, but the./MyService module contained order-sensitive side effects. By setting the new--importsNotUsedAsValues flag topreserve, import declarations would not be elided, and the module loading order and side effects could be preserved. Type-only imports could then be used to elide specific import declarations.

Background:preserveValueImports

preserveValueImports was added in#44619 as a way to control elision of individual imported names so that symbols can be referenced from places TypeScript cannot analyze, likeeval statements or Vue templates:

import{doSomething}from"./module";eval("doSomething()");

Under default compiler options, the entire import statement is removed, so the eval’d code fails. Under--importsNotUsedAsValues preserve, the import declaration is preserved asimport "./module" since the flag is only concerned with module loading order and potential side effects that may be contained in"./module". Under the new--preserveValueImports option,doSomething would be preserved even though the compiler thinks it is unused.

In the same release, the ability to mark individual import specifiers as type-onlywas added as a complement to--preserveValueImports.

User feedback

These two flags, along with type-only import syntax, were designed to solve fairly niche problems. Early on, I encouraged users not to use type-only imports unless they were facing one of those problems. But as soon as they were available, and consistently since then, we have seen enthusiasm for adopting type-only imports everywhere possible as an explicit marker of what imports will survive compilation to JS. But since the flags were not designed to support that kind of usage of type-only imports, the enthusiasm has been accompanied by confusion around the configuration space and frustration that auto-imports, error checking, and emit don’t align with users’ mental model of type-only imports.

Further, because the two flags were designed at different times to address different issues, they interact with each other (and withisolatedModules) in ways that are difficult to explain without diving into the background of each flag and the narrow problems they were intended to solve. And the flag names do nothing to clear up this confusion.

Proposal

We can solve the problems addressed byimportsNotUsedAsValues andpreserveValueImports with a single flag that is

  • easier to explain
  • less complex to implement
  • well-suited to users who want to use type-only imports for stylistic reasons

On the schedule of#51000, I propose deprecatingimportsNotUsedAsValues andpreserveValueImports, and replacing them with a single flag called (bikesheddable)verbatimModuleSyntax. The effect ofverbatimModuleSyntax can be described very simply:

verbatimModuleSyntax: Emits imports and exports to JS outputs exactly as written in input files, minus anything marked as type-only. Includes checks to ensure the resulting output will be valid.

No elision withouttype

This is a stricter setting than eitherimportsNotUsedAsValues orpreserveValueImports (though it’s approximately what you get by combining both withisolatedModules), because it requires that all types be marked as type-only. For example:

import{writeFile,WriteFileOptions}from"fs";

would be an error in--verbatimModuleSyntax becauseWriteFileOptions is only a type, so would be a runtime error if emitted to JS. This import would have to be written

import{writeFile,typeWriteFileOptions}from"fs";

No transformations between module systems

True to its name,verbatimModuleSyntax has another consequence: ESM syntax cannot be used in files that will emit CommonJS syntax. For example:

import{writeFile}from"fs";

This import is legal under--module esnext, but an error in--module commonjs. (Innode16 andnodenext, it depends on the file extension and/or the package.json"type" field.) If the file is determined to be a CommonJS module at emit by any of these settings, it must be written as

importfs= require("fs");

instead. Many users have the impression that this syntax is legacy or deprecated, but that’s not the case. It accurately reflects that the output will use arequire statement, instead of obscuring the output behind layers of transformations and interop helpers. I think using this syntax is particularly valuable in.cts files under--module nodenext, because in Node’s module system, imports and requires have markedly different semantics, and actually writing outrequire helps you understand when and why you can’trequire an ES module—it’s easier to lose track of this when yourrequire is disguised as an ESMimport in the source file.

Metadata

Metadata

Assignees

Labels

CommittedThe team has roadmapped this issueFix AvailableA PR has been opened for this issueSuggestionAn idea for TypeScript

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions


    [8]ページ先頭

    ©2009-2025 Movatter.jp