Sebastian
Sebastian

Reputation: 3594

Is `import type` equivalent to `import()`

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

Answers (1)

Andrew
Andrew

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.

(Playground link)

Upvotes: 15

Related Questions