Bird
Raised Fist0
Typescriptprogramming~15 mins

Type-only imports and exports in Typescript - Deep Dive

Choose your learning style10 modes available
Overview - Type-only imports and exports
What is it?
Type-only imports and exports in TypeScript allow you to import or export types without including any actual code in the final JavaScript output. This means you can use types for checking and development without affecting the runtime. It helps keep your code clean and efficient by removing unnecessary imports when the code runs.
Why it matters
Without type-only imports and exports, TypeScript would include all imported modules in the JavaScript output, even if you only need their types. This can cause larger files and slower loading times. Type-only imports solve this by ensuring that type information is used only during development and removed when the code runs, improving performance and reducing bundle size.
Where it fits
Before learning type-only imports and exports, you should understand basic TypeScript types and how regular imports and exports work. After this, you can explore advanced module management, declaration merging, and how TypeScript integrates with build tools like bundlers.
Mental Model
Core Idea
Type-only imports and exports bring in or share type information without adding any runtime code.
Think of it like...
It's like borrowing a recipe from a friend to understand how to cook a dish, but you don't take their kitchen tools home with you.
┌───────────────────────────────┐
│       TypeScript Module        │
│ ┌───────────────┐             │
│ │ Type-only     │             │
│ │ import/export │───► (removed)│
│ └───────────────┘             │
│ ┌───────────────┐             │
│ │ Regular       │             │
│ │ import/export │───► Included│
│ └───────────────┘             │
└───────────────────────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding TypeScript Imports
🤔
Concept: Learn how TypeScript imports bring in code and types from other files.
In TypeScript, you can import variables, functions, classes, and types from other files using the import statement. For example: import { User } from './models'; This brings in the User type or class from the models file. Normally, both types and code are imported together.
Result
You can use imported items in your code, and both type information and runtime code are included in the output.
Knowing how imports work is essential because type-only imports are a special case of this mechanism.
2
FoundationDifference Between Types and Values
🤔
Concept: Types exist only during development, while values exist at runtime.
Types describe the shape or structure of data but do not exist when the program runs. Values like variables and functions exist both during development and runtime. For example: type User = { name: string }; const user: User = { name: 'Alice' }; Here, User is a type, and user is a value.
Result
You understand that types help with checking but do not produce code in JavaScript output.
Separating types from values is key to understanding why type-only imports can be removed from the final code.
3
IntermediateUsing Type-only Imports
🤔Before reading on: do you think importing a type normally adds code to the final JavaScript? Commit to your answer.
Concept: Type-only imports bring in types without adding any runtime code to the output.
TypeScript lets you import types using the 'import type' syntax: import type { User } from './models'; This means User is only used for type checking and will not appear in the JavaScript output. It helps keep the code smaller and faster.
Result
The JavaScript output excludes any code related to the imported type, reducing bundle size.
Understanding that 'import type' removes runtime code helps prevent accidental code bloat.
4
IntermediateUsing Type-only Exports
🤔Before reading on: do you think exporting a type normally includes code in the output? Commit to your answer.
Concept: Type-only exports share types without exporting any runtime code.
You can export types using 'export type' syntax: export type User = { name: string }; This exports only the type information. When another file imports this type, it can use 'import type' to avoid runtime code inclusion.
Result
Only type information is shared, no JavaScript code is generated for these exports.
Knowing how to export types separately helps organize code and avoid unnecessary runtime dependencies.
5
IntermediateMixing Type-only and Regular Imports
🤔Before reading on: if you import both types and values from the same module, does 'import type' affect the values? Commit to your answer.
Concept: You can combine type-only imports with regular imports to control what appears at runtime.
Example: import type { User } from './models'; import { createUser } from './models'; Here, User is imported only for types, so no code is added for it. createUser is a function, so it is included in the output. This separation helps optimize the final code.
Result
Only the necessary runtime code is included, while type information is used for checking only.
Separating types and values in imports gives fine control over code size and dependencies.
6
AdvancedHow Type-only Imports Affect Tree Shaking
🤔Before reading on: do you think type-only imports improve tree shaking in bundlers? Commit to your answer.
Concept: Type-only imports help bundlers remove unused code more effectively by excluding types from runtime bundles.
Tree shaking is a process where bundlers remove unused code. Since type-only imports do not generate code, bundlers can focus on actual runtime code. This reduces bundle size and improves performance. Without type-only imports, bundlers might keep unused code because they see imports as runtime dependencies.
Result
Smaller, faster bundles with only necessary code included.
Understanding the interaction between type-only imports and bundlers helps write more efficient production code.
7
ExpertSubtle Behavior with Type-only Imports and Namespace Imports
🤔Before reading on: do you think 'import type * as' imports behave exactly like regular namespace imports? Commit to your answer.
Concept: Type-only namespace imports import all types under a namespace without runtime code, but mixing with values can cause errors.
Example: import type * as Models from './models'; This imports only types under Models. However, if you try to use Models as a value at runtime, it causes errors because no code exists. Mixing type-only namespace imports with value usage requires careful separation to avoid runtime failures.
Result
You get type safety without runtime code, but must avoid using type-only namespaces as values.
Knowing this subtlety prevents confusing runtime errors and helps maintain clean separation of types and values.
Under the Hood
TypeScript's compiler distinguishes between types and values during compilation. When it sees 'import type' or 'export type', it treats these as purely compile-time constructs. It removes all related import or export statements from the emitted JavaScript code. This means no require or import calls appear for these types, so they do not increase runtime size or affect execution.
Why designed this way?
Originally, TypeScript imported types and values together, causing unnecessary code in JavaScript output. To optimize performance and bundle size, the language introduced type-only imports and exports. This design balances strong type safety with efficient runtime code, avoiding bloated bundles and improving developer experience.
┌───────────────┐       ┌───────────────┐
│ TypeScript    │       │ JavaScript    │
│ Source Code   │       │ Output       │
├───────────────┤       ├───────────────┤
│ import type { │       │ (removed)     │
│ User } from   │──────▶│               │
│ './models';   │       │               │
│ import { fn } │       │ import { fn } │
│ from './mod'; │──────▶│ from './mod'; │
└───────────────┘       └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does 'import type' bring runtime code into the JavaScript output? Commit to yes or no.
Common Belief:People often think 'import type' still adds code to the final JavaScript because it looks like a normal import.
Tap to reveal reality
Reality:'import type' imports only type information and is completely removed from the JavaScript output.
Why it matters:Believing otherwise can lead to confusion about bundle size and performance, causing developers to avoid using type-only imports.
Quick: Can you use a type-only import as a value at runtime? Commit to yes or no.
Common Belief:Some think that type-only imports can be used as normal values in code.
Tap to reveal reality
Reality:Type-only imports do not exist at runtime, so using them as values causes errors.
Why it matters:Misusing type-only imports as values leads to runtime crashes and bugs that are hard to debug.
Quick: Does exporting a type with 'export type' include code in the output? Commit to yes or no.
Common Belief:Many believe that exporting types also exports runtime code.
Tap to reveal reality
Reality:'export type' exports only type information and does not generate any JavaScript code.
Why it matters:Misunderstanding this can cause developers to overcomplicate module exports or avoid exporting types properly.
Quick: Does mixing type-only imports with regular imports from the same module cause duplication in output? Commit to yes or no.
Common Belief:Some think that importing types and values separately from the same module duplicates code in the output.
Tap to reveal reality
Reality:Type-only imports are removed, so only one runtime import remains, avoiding duplication.
Why it matters:This misconception can prevent developers from optimizing imports and keeping code clean.
Expert Zone
1
Type-only imports can improve incremental build times because the compiler can skip generating code for them.
2
Using 'import type' helps avoid circular dependencies at runtime by breaking value import cycles while keeping type safety.
3
Type-only exports allow creating public API surfaces that expose only types, hiding implementation details and improving encapsulation.
When NOT to use
Avoid type-only imports when you need runtime values or side effects from a module. Use regular imports for code that must run. Also, in some legacy build setups, type-only imports may cause issues; in those cases, consider using legacy import patterns carefully.
Production Patterns
In large codebases, teams use type-only imports to separate API types from implementation code, enabling faster builds and smaller bundles. Libraries often export types separately with 'export type' to allow consumers to import types without pulling in code. Bundlers and linters are configured to enforce type-only imports for pure types.
Connections
Tree Shaking
Type-only imports support tree shaking by removing unused code.
Understanding type-only imports clarifies how bundlers eliminate dead code, improving app performance.
Dependency Injection
Both separate concerns: type-only imports separate type info from runtime code, dependency injection separates configuration from logic.
Knowing this helps appreciate modular design and clean architecture principles.
Compile-time vs Runtime in Programming Languages
Type-only imports exist only at compile-time, similar to how some languages separate compile-time checks from runtime behavior.
Recognizing this distinction deepens understanding of language design and optimization.
Common Pitfalls
#1Using a type-only import as a runtime value causes errors.
Wrong approach:import type { User } from './models'; const userInstance = new User(); // Error: User is not a value
Correct approach:import { User } from './models'; const userInstance = new User(); // Correct usage
Root cause:Confusing types with values leads to using type-only imports where runtime code is needed.
#2Forgetting to use 'import type' for pure types causes unnecessary code in output.
Wrong approach:import { User } from './models'; // User is only a type function greet(user: User) { console.log(user.name); }
Correct approach:import type { User } from './models'; function greet(user: User) { console.log(user.name); }
Root cause:Not distinguishing between types and values causes bloated bundles.
#3Mixing type-only namespace imports with value usage causes runtime errors.
Wrong approach:import type * as Models from './models'; const user = new Models.User(); // Error: Models is not a value
Correct approach:import * as Models from './models'; const user = new Models.User(); // Correct usage
Root cause:Using type-only namespace imports as values misunderstands their compile-time-only nature.
Key Takeaways
Type-only imports and exports let you use types without adding runtime code, keeping JavaScript output clean and efficient.
Separating types from values helps reduce bundle size and improves application performance.
Using 'import type' and 'export type' correctly prevents common bugs and runtime errors related to missing values.
Understanding the difference between compile-time types and runtime values is essential for effective TypeScript programming.
Expert use of type-only imports supports better build times, cleaner APIs, and improved modularity in large projects.

Practice

(1/5)
1. What is the main purpose of using import type in TypeScript?
easy
A. To export types from a module.
B. To import both types and runtime values together.
C. To import only runtime values and ignore types.
D. To import only types without including any runtime code.

Solution

  1. Step 1: Understand the role of import type

    This syntax imports only type information, not actual code that runs.
  2. Step 2: Compare with normal imports

    Normal imports bring in both types and runtime code, but import type excludes runtime code.
  3. Final Answer:

    To import only types without including any runtime code. -> Option D
  4. Quick Check:

    Type-only imports = import type [OK]
Hint: Remember: import type brings types only, no code. [OK]
Common Mistakes:
  • Thinking import type imports runtime code
  • Confusing import type with normal import
  • Using import type to export types
2. Which of the following is the correct syntax to export only types from a TypeScript module?
easy
A. export { MyType } from './types';
B. export type { MyType } from './types';
C. export import type MyType from './types';
D. export only type MyType from './types';

Solution

  1. Step 1: Identify correct export type syntax

    TypeScript uses export type { ... } to export only types.
  2. Step 2: Check other options

    export { MyType } from './types'; (export { MyType } from './types';) exports both types and values. Options B and C are invalid syntax.
  3. Final Answer:

    export type { MyType } from './types'; -> Option B
  4. Quick Check:

    Type-only export = export type [OK]
Hint: Use export type {} to export types only. [OK]
Common Mistakes:
  • Using normal export instead of export type
  • Writing invalid syntax like export only type
  • Confusing import syntax with export syntax
3. Consider the following code snippet:
import type { User } from './models';

function greet(user: User) {
  console.log(`Hello, ${user.name}`);
}

greet({ name: "Alice" });

What will happen when this code is compiled and run?
medium
A. It will print 'Hello, ' followed by the user's name at runtime.
B. It will cause a runtime error because User is not imported as code.
C. It will fail to compile because import type cannot be used with functions.
D. It will import the full User class including methods.

Solution

  1. Step 1: Understand import type usage

    The import type imports only type info, which is erased at runtime.
  2. Step 2: Check runtime behavior

    The function uses User only as a type for parameter. At runtime, it expects an object with a name property.
  3. Final Answer:

    It will print 'Hello, ' followed by the user's name at runtime. -> Option A
  4. Quick Check:

    Type-only import works for typing, no runtime error [OK]
Hint: Type-only imports vanish at runtime but keep type safety. [OK]
Common Mistakes:
  • Expecting runtime import of types
  • Thinking import type causes runtime errors
  • Confusing compile-time types with runtime values
4. Identify the error in this TypeScript code:
import type { Config } from './config';

export { Config };
medium
A. You cannot export a type that was imported with import type.
B. The import type statement is missing a semicolon.
C. You must use export type { Config } to export the type correctly.
D. There is no error; this code works fine.

Solution

  1. Step 1: Check import and export compatibility

    Types imported with import type must be exported with export type.
  2. Step 2: Identify correct export syntax

    Using normal export { Config } tries to export a runtime value, which does not exist.
  3. Final Answer:

    You must use export type { Config } to export the type correctly. -> Option C
  4. Quick Check:

    Type-only export matches type-only import [OK]
Hint: Match import type with export type. [OK]
Common Mistakes:
  • Exporting type-only imports as normal exports
  • Ignoring that types do not exist at runtime
  • Confusing semicolon errors with syntax errors
5. You have a module shapes.ts exporting types and values:
export type Circle = { radius: number };
export const PI = 3.14;

You want to import only the type Circle in another file without importing PI. Which is the best way?
hard
A. import type { Circle } from './shapes';
B. import { Circle } from './shapes';
C. import { Circle, PI } from './shapes';
D. import type { Circle, PI } from './shapes';

Solution

  1. Step 1: Understand import type behavior

    import type imports only types, no runtime values like PI.
  2. Step 2: Check options for importing only Circle

    import type { Circle } from './shapes'; imports only the type Circle without bringing in PI.
  3. Final Answer:

    import type { Circle } from './shapes'; -> Option A
  4. Quick Check:

    Use import type to avoid importing values [OK]
Hint: Use import type to import only types, no values. [OK]
Common Mistakes:
  • Using normal import and importing unwanted values
  • Trying to import values with import type
  • Importing both types and values when only types are needed
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard