Implicit module import/export elision · Issue #2812 · microsoft/TypeScript (original) (raw)
Current behavior
The compiler today elides imports and exports that are types or not referenced in a value position in the body of the importing module. See spec sections section 11.2.5 and section 11.2.6 for more details.
This has allowed for using the same syntax to import types and/or namespaces the same way values are imported. This has been convenient but has caused some problems:
- It has been consistently a source of confusion for TypeScript users (see issues: Imports left out of module dependencies when not directly assigned or assigned to. #2132, define parameter not generated for a referenced AND used external module class. #2038, and force import #596).
- It impedes single-file-transpilation (see Support single-module transpilation mode #2499) as there is no way to know if a re-export (e.g. t in
export { t } from "mod"
) should be elided or not without looking at the whole program
Proposed change
1. Do not elide module imports even if they are only used in type position.
Even if the import is not used, keep the module dependency (i.e. the require() call) to ensure any side effects are maintained.
e.g.:
import { InterfaceFoo } from "foo"; import { ClassBar } from "bar";
var x: InterfaceFoo = new ClassBar();
emits in ES6:
import {} from "foo"; import { ClassBar } from "bar"; var x = new ClassBar();
and ES5/commonjs:
require("foo"); // import but do not capture var bar_1 = require("bar"); var x = new bar_1.ClassBar();
2. Exports with no value side are always elided
This is the existing behavior, an export to an interface or a non-instantiated module will be elided.
e.g.:
interface I {
}
export { I }; // Elided along with the interface declaration
3. A type
modifier is required for type-only imports and exports
The type
modifier will cause the import to alias the type side of the imported entity, and would indicate the intent for this import statement to be always elided.
import type { IFoo } from "mod1"; // import to "mod1" will be elided
export type { IBar } from "mod2"; // similarly, export statement will be elided
Optionally type
can be applied to specific named bindings in export and import declarations, which will result in eliding the import if all its bindings are types.
import {type IFoo, type IBar} from "mod"; // import will be elided
Errors:
- It is an error to use
type
on a value-only import/export (e.g. var declaration). - It is an error to import or re-export a type only name without a
type
modifier
import { Interface, Class, Enum } from "foo"; /// Error: import Interface
does not have a value side
/// and should be marked as type.
export { type } from "bar"; /// Error: export type
does not have a value side
/// and should be marked as type.
- Similarly
default
exports must have a value side:
interface IFoo { } export default IFoo; /// Error: export default target must have a value.
Implications to import =
/ export =
syntax
The proposal only affects the new ES6-style import/export syntax. Existing elision rules for import id = require("module")
and export = id
are not changed.