Skip to content

request: add { importsNotUsedAsValues: preserve-values } to compilerOptions #43687

Closed
@milahu

Description

@milahu

Suggestion

im looking for a { importsNotUsedAsValues: 'preserve-values' } compiler-option, where

1. all type-imports are removed, including "empty imports" like import "./some" and import "./where.d.ts"
2. unused imports are preserved, including maybe-type-imports

this would make it much easier to compile code-fragments in isolation
where ts has no access to other files, and no access to the fragment's context

1. type-imports should be fully removed

import { SomeType } from "./types"
var someVar: SomeType; // here ts can see that SomeType is a type

// ... is currently transpiled to ...

import "./types";
var someVar;

2. unused imports should be preserved

import { NotUsed } from "./mixed"
// NotUsed is not used anywhere, so its value or type

// ... is currently transpiled to ...

import "./mixed";

to support the infamous export { SomeThing } from './some-where' case
where ts.transpileModule cannot tell whether SomeThing is a type or value:

use a fault-tolerant module loader, like

// note: top level await only works in modules
var importedModule = await (async () => {
  try { return await import('./some-module.js'); }
  catch (error) { console.warn(error.message); }
  return {};
})();

this is needed for development mode, where tree-shaking is disabled
so imports from type files would give fatal errors

deprecated: transpile type exports to "dummy exports"

edit: this is deprecated, since it breaks with external modules

external modules are not compiled in the current build pipeline
so there is no way we can generate "dummy exports" for all maybe-type-imports

in case SomeThing is a type
ts.transpileModule would have to generate "dummy exports"
like export const SomeThing = null; in some-where.js

... to avoid the runtime error SomeThing is not exported by some-where
or no such file: some-where

export type SomeType = number;

// ... would be transpiled to ...

export const SomeType = null;

adding such dummy exports should be safe
since collisions between value IDs and type IDs are not possible in ts
and IDs are (usually) scoped to modules

this solution is "clean", cos the added dead-code is removed later in the build pipeline (tree-shaking in production mode)

compiler call (playground on codesandbox)

var result = ts.transpileModule(source, {
  fileName: "test.ts",
  compilerOptions: {
    target: "es2015",
    importsNotUsedAsValues: "preserve-values", // new
  }
});

🔍 Search Terms

single file compilation
ts.transpileModule
remove only type imports
keep unused value imports
generate dummy exports for types

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
    • yes: the "dummy exports" would be removed later in the build pipeline (dead code elimination, tree shaking)
      • yes: goal 4. Emit clean, idiomatic, recognizable JavaScript code.
      • yes: goal 7. Preserve runtime behavior of all JavaScript code.
      • yes: goal 9. Use a consistent, fully erasable, structural type system.
    • yes: non-goal 4. Provide an end-to-end build pipeline. Instead, make the system extensible so that external tools can use the compiler for more complex build workflows.

⭐ Suggestion

📃 Motivating Example

💻 Use Cases

references

single file transpile: sveltejs/svelte-preprocess#318 (comment)
dummy exports: https://stackoverflow.com/a/52260432/10440128
api docs: https://www.typescriptlang.org/tsconfig#importsNotUsedAsValues

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions