[Typescript] Verbatim Module Syntax Enforces Import Type
One of the interesting things about verbatimModuleSyntax
in TypeScript is that it requires a specific type of import when you're working with types. Let's take a look.
In our tsconfig.json
file, we have the verbatimModuleSyntax
option set to true
:
// inside tsconfig.json
{
"compilerOptions": {
...
"verbatimModuleSyntax": true
...
We're working with two files: index.ts
and example.ts
.
Inside of example.ts
is a type called User
:
// inside example.ts
export type User = {
name: string;
age: number;
};
In index.ts
, we're trying to import User
from example.js
. However, we have an error:
// inside index.ts
import { User } from './example.js'; // red squiggly line under `User`
// hovering over `User` shows:
User is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.
import type {User} from "./user"
If we start writing import type
, we get an autocomplete suggestion for User
. This import type
syntax is great for working with types when verbatimModuleSyntax
is enabled.
This syntax tells TypeScript that imported line should be completely removed from the compiled JavaScript output. That's because types are only a TypeScript thing; they don't exist in JavaScript.
We can see this if we look at the generated index.js
file – there are no imports at all:
// inside dist/index.js
export {};
If we were to change our import back to import { User } from "./example.js";
, without the type
keyword, it would get included in the outputted JavaScript, even though User
doesn't exist in example.js
:
// inside dist/index.js
import { User } from "./example.js";
export {};
This is where the "verbatim" part of "verbatim module syntax" comes in- the import gets included "as is" in the output.
Another Option
We could actually apply the type
keyword directly to the named import like this:
import { type User } from "./example.js";
Let's look at an example of how this behavior works.
Inside of example.ts
, let's add a console.log
statement:
// example.ts
export type User = {
name: string;
age: number;
};
console.log("hello!");
When using import { type User }
, only the User
type is imported.
However, if we have just import { User }
without type
, any top-level code in example.js
like the console log statement will execute immediately. This means that simply by importing index.js
, we'd also be running the console.log
in example.js
.
Using import type
allows us to import things from the file, but specify that only the named imports are treated as types without side effects.