When to use Type Only Imports Exports in TypeScript?
4 min read

Since the introduction of type-only imports in TypeScript 3.8, a lot of developers including me have been confused about the usage of type-only imports. If we read the official docs, it says -
import typeonly imports declarations to be used for type annotations and declarations. It always gets fully erased, so there’s no remnant of it at runtime.Similarly,
export typeonly provides an export that can be used for type contexts, and is also erased from TypeScript’s output.
But isn't typescript code supposed to cease automatically at runtime? Why do we need type-only imports?
To understand this, First, we need to understand how single-file transpilers (like Babel) work.
Suppose we have the following 3 files -
Component.tsximport { Props } from "./types"; const Component = (props: Props) => { return <div>Component</div>; }; export { Component };types.tsexport type Props = { a: number; b: string; };index.tsimport { Component } from "./Component"; import { Props } from "./types"; export { Component, Props };
Now, let's see what will the corresponding output JS files look like when they are passed through a single file transpiler -
Component.js"use strict"; exports.__esModule = true; exports.Component = void 0; var Component = function (props) { return <div>Component</div>; }; exports.Component = Component;types.js"use strict"; exports.__esModule = true;index.js"use strict"; exports.__esModule = true; exports.Props = exports.Component = void 0; var Component_1 = require("./Component"); exports.Component = Component_1.Component; var types_1 = require("./types"); exports.Props = types_1.Props;
If we compare the above outputs, we can see that all the typescript-only code (types and imports) has been successfully removed in Component.tsx and types.ts file but not in index.js file.
If we run this JS code, we will get a runtime error ❗️ since there is no named export Props in types.js file
Now, let's see how our transpiler was able to remove typescript-only code in each of the files -
Component.tsx- Here, the importPropsis being used as atypein the file itself, So it is able to deduce thatPropsis indeed a type and can safely remove it.import { Props } from "./types"; 👇 const Component = (props: Props) => {types.ts- In this file, we have used thetypekeyword to declarePropstype. So, it can also be safely removed.👇 export type Props = {index.ts- In this file, we are simply importing and re-exportingComponentandProps. Since single-file transpilers work only on a single file at a time, there is no way to know whether these are types or variables or functions, etc.So, they both are kept in the output js file.
import { Component } from "./Component"; import { Props } from "./types"; ❔❔❔ export { Component, Props };
To fix this, we can mark Props import as type-only -
import { Component } from "./Component";
import type { Props } from "./types";
export { Component, Props };
Now, our transpiler knows for sure that Props is a type and can safely omit it.
Note: This problem only occurs when we are using a single file transpiler such as babel since they only operate on a single file at a time and can't apply code transforms that depend on understanding the full type system.
TypeScript also provides us with a flag isolatedModules to avoid writing certain code (like the above) that can cause issues when using a single-file transpiler.
If we turn on the isolatedModules flag, we will get the following error in index.ts file
import { Component } from "./Component";
import { Props } from "./types";
// ❗️❗️ Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'. ❗️❗️
export { Component, Props };
So, to summarize this 🔖-
If we are importing a
typeand using it in the same file, we do not need to usetypesince even a single file transpiler can understand from the context that it is atype.But, If we are only exporting the
typeand not using it, we need to make it atype-onlyimport to explicitly tell the transpiler that it is a type.We should turn on the
isolatedModulesflag to avoid such issues.