Reputation: 3594
Are
import type { Foo, Bar as Baz } from './'
and
type Foo = import('./').Foo
type Bar = import('./').Baz
equivalent?
Please be aware that the import()
in this context is not a dynamic import but import
types introduced in TypeScript 2.9. You can enter both examples into the TypeScript playground to verify that the syntax/types are valid.
I want to use import type
but also create declarations for TS < 3.8.
If the above assertion is true I could apply an AST transform to the emitted declaration files.
Upvotes: 12
Views: 9126
Reputation: 5126
They are not equivalent, though they are often interchangeable in practice. There are two differences that can matter, depending on the context.
import type
makes a file a module.The TypeScript compiler determines whether a TypeScript file’s top level scope is module-scoped or globally scoped (i.e., whether the file is a module or a script) by the presence of any import
or export
declarations. import type
counts as an import declaration, so if it were the only import/export statement in the file, substituting a type alias for it would change the file from a module to a script. The practical implication of this is that if you have a file (usually a .d.ts file) that you need to be global, but want to reference types from a module, you’re stuck using type Foo = import(...)
.
import type
can reference value and namespace meanings.When you declare a type alias with type Foo = import('./').Bar
, Foo
is purely a type, even if Bar
had both a type and a value meaning (e.g. class Bar {}
). You have completely left the value side of Bar
behind in this declaration. import type
actually references the full Bar
symbol and all its meanings, it just sets up a syntactic check that ensures you never use it in a position that would get emitted to JS (since the import statement will get erased in JS). This matters in a few places, easiest to show with an example:
import type { EventEmitter as EE1 } from "events";
type EE2 = import("events").EventEmitter;
// Ok, because `declare` means `Foo1` is not actually referenced
// in an emitting position.
declare class Derived1 extends EE1 {}
declare class Derived2 extends EE2 {}
// ^^^
// Error: 'EE2' only refers to a type, but is being used as a value here.
type EventEmitterCtor1 = typeof EE1; // Ok
type EventEmitterCtor2 = typeof EE2;
// ^^^
// Error: 'EE2' only refers to a type, but is being used as a value here.
const x = EE1;
// ^^^
// Error: 'EE1' cannot be used as a value because it was imported using 'import type'
const y = EE2;
// ^^^
// 'EE2' only refers to a type, but is being used as a value here.
Upvotes: 15