import type
Sometimes the next room needs not the thing itself but only its size. To sew a cover for a sofa, nobody carries the sofa around the house: a slip of paper with its measurements is enough. The slip is needed while the cover is being sewn, and once the cover is ready, the slip is thrown away.
Types in modules are such measurements. Say a program has three files:
user.tsexports an interfaceUserwith a first and a last name;format.tsexports a functionformatName(user: User): stringthat joins the first and last name;main.tstakes both the interface and the function.
The function formatName works at run time: it is called and returns a string. The interface User is needed only by tsc, to check the code. Here is user.ts:
export interface User {
first: string;
last: string;
}Importing only types
For measurements TypeScript has a separate form, a type-only import: import type { User } from "./user.js";. Let's take it apart:
import typeis an import that brings only types;{ User }holds the type names, and there may be several;from "./user.js"names the module that exports those types.
import type is used for interfaces and type aliases. You can take a class this way too, but then it is only a type: you cannot create an object with new.
Let's see what tsc does with a file main.ts that has both kinds of import. Press "Next step" and watch which lines make it into the JavaScript.
The import type line is gone entirely: it brought only the type User.
What is left in JavaScript
After compilation, main.js:
- has no
import typeline: it is gone entirely; - has no interfaces or type aliases such as
Badge; - has no annotations on parameters, results or variables;
- keeps functions, classes and
enum: they work at run time; - keeps ordinary value imports such as
import { formatName }.
Here the measurement picture stops working: someone has to throw the slip away, while tsc erases the types from the JavaScript on its own.
One more detail: main.js has no "use strict"; line at the top. A JavaScript module always runs in strict mode anyway, so tsc does not add it.
Why write type
The compiler also removes an ordinary import import { User } from "./user.js" when User appears in the file only as a type. But to find that out, it has to look into user.ts and check every place with that name. import type writes the answer right in the line, and that gives three things:
- A reader of the code sees at once that the line will not survive into JavaScript.
tscholds you to the promise: a name fromimport typecannot be used as a value.- A tool that sees one file knows the line can be removed. That is how
node main.tsworks: it cuts out the types without looking into other files. It keeps an ordinary import ofUser, and at run time Node looks for a valueUserthe module does not have: the program stops with an error.
The second point in code: a function was taken with import type and then called.
import type { formatName } from "./format.js";
const text = formatName({ first: "Anna", last: "Ivanova" }); // ✗error TS1361: 'formatName' cannot be used as a value because it was imported using 'import type'.tsc reminds you: such an import disappears from the JavaScript, and main.js will have no function formatName.
Interviewers often ask how import type differs from an ordinary import. The answer: import type brings only types and disappears from the JavaScript entirely, while a value import stays and loads the module at run time.
The .js extension in the path
tsc does not change the path after from: the string "./format.js" reaches main.js as it is. At run time main.js loads the neighboring file format.js, the one tsc writes from format.ts. That is why the path in the source names the future .js file. While checking types, tsc knows that format.ts stands behind ./format.js and reads it.
Common mistake
The path was written without an extension:
import { formatName } from "./format"; // ✗
console.log(formatName({ first: "Anna", last: "Ivanova" }));tsc main.ts stays silent: with the default settings it finds format.ts by such a path too. But node main.js stops:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../format' imported from .../main.jsNode.js looks for the file exactly by the path in main.js, and there is no file format without an extension: the file next to it is format.js. In place of the dots Node.js prints the full path to the project folder. Add the extension: from "./format.js".
Key points
import type { User } from "./user.js"brings only types and disappears from the JavaScript entirely.- Every type disappears from the JavaScript: interfaces, aliases, annotations; functions, classes,
enumand value imports stay. - A name from
import typecannot be used as a value:tscanswers with errorTS1361. tscdoes not change an import path, so the path carries the extension of the future file:./format.js.