LogicLoop Logo
LogicLoop
LogicLoop / clean-code-principles / Why TypeScript Requires File Extensions in Import Statements: The Complete Guide
clean-code-principles June 2, 2025 4 min read

Why TypeScript Requires File Extensions in Import Statements and How to Configure Your Projects

Jamal Washington

Jamal Washington

Infrastructure Lead

Why TypeScript Requires File Extensions in Import Statements: The Complete Guide

If you've recently upgraded your TypeScript project, you might have encountered a surprising error message when trying to import modules without file extensions. TypeScript is now enforcing a change in how you write import statements, and while it might seem like an annoying requirement at first, there are good reasons behind this shift.

The Problem: Missing File Extensions in Imports

For years, developers have been used to writing imports like this:

TYPESCRIPT
import example from 'foo';
1

But with newer TypeScript configurations, particularly when using the 'node16' or 'nodenext' module resolution strategy, you'll encounter this error:

TEXT
Relative import paths need explicit file extensions in ES imports when module resolution is node16 or nodenext.
1

The Incorrect Solution: Adding .ts Extensions

Your first instinct might be to add the TypeScript extension to the import path:

TYPESCRIPT
import example from 'foo.ts';
1

But this leads to another error:

TEXT
An import path can only end with a .ts extension when allowImportingTsExtensions is enabled.
1

The Correct Solution: Adding .js Extensions

Surprisingly, the correct solution is to add a .js extension to your imports, even when you're importing from TypeScript files:

TYPESCRIPT
import example from 'foo.js';
1

This might seem counterintuitive since you're referencing a file that doesn't actually exist yet (your TypeScript file will be compiled to JavaScript later). However, this approach aligns with how the compiled code will eventually run.

Why TypeScript Requires Explicit File Extensions

There are two main reasons behind this change:

  1. Performance improvements in Node.js applications
  2. Alignment with the actual runtime environment

When Node.js encounters an import without an extension, it has to try multiple resolution strategies (checking for .js, index.js, etc.). By specifying the exact file extension, Node.js can load modules faster without this resolution overhead.

Modern bundlers process your code more efficiently with explicit file extensions
Modern bundlers process your code more efficiently with explicit file extensions

As for why .js instead of .ts extensions - remember that your TypeScript code will be compiled to JavaScript before execution. If you were to use .ts extensions in your imports, those paths wouldn't exist in the compiled output, causing runtime errors when Node.js tries to load them.

Configuration Options for File Extensions in TypeScript

If you prefer not to use explicit .js extensions in your imports, you have a few configuration options:

Option 1: Use the 'bundler' Module Resolution

In your tsconfig.json, you can change the moduleResolution setting:

JSON
{
  "compilerOptions": {
    "moduleResolution": "bundler"
    // other options...
  }
}
1
2
3
4
5
6

The 'bundler' resolution strategy is designed for projects that use bundlers like esbuild, webpack, or swc. These tools handle more complex module resolution and transformations automatically.

Using bundler module resolution lets you omit file extensions in imports
Using bundler module resolution lets you omit file extensions in imports

Option 2: Enable allowImportingTsExtensions

If you really want to use .ts extensions in your imports, you can enable the allowImportingTsExtensions option, but this comes with important caveats:

JSON
{
  "compilerOptions": {
    "allowImportingTsExtensions": true,
    "noEmit": true
    // other options...
  }
}
1
2
3
4
5
6
7

Note that this requires setting noEmit to true, meaning TypeScript won't generate output files. This approach is typically only useful for projects that use another tool for transpilation.

Understanding the Trade-offs

While adding .js extensions to imports may feel like an extra step, it offers several benefits:

  • Faster module resolution at runtime
  • Clearer mapping between source and compiled code
  • More explicit dependencies that help tooling and developers understand the codebase
  • Better compatibility with ESM (ECMAScript Modules)
Modern JavaScript tooling performs advanced transformations on your import statements
Modern JavaScript tooling performs advanced transformations on your import statements

Best Practices for TypeScript Import Extensions

Based on the current TypeScript ecosystem, here are some recommendations:

  • For Node.js applications using ESM, embrace explicit .js extensions
  • For frontend applications using bundlers, consider the 'bundler' moduleResolution option
  • Use path aliases in tsconfig.json to reduce the verbosity of import paths
  • Consider using tools like eslint-plugin-import to enforce consistent import patterns
  • Document your project's import convention to maintain consistency across the team

Conclusion

While requiring explicit file extensions in TypeScript imports might seem like an unnecessary change at first, it aligns with modern JavaScript practices and improves runtime performance. By understanding the reasoning behind this change and the available configuration options, you can choose the approach that best suits your project's needs.

Whether you decide to embrace .js extensions in your imports or configure TypeScript to work without them, maintaining consistency throughout your codebase will help ensure a smoother development experience for everyone on your team.

Let's Watch!

Why TypeScript Requires File Extensions in Import Statements: The Complete Guide

Ready to enhance your neural network?

Access our quantum knowledge cores and upgrade your programming abilities.

Initialize Training Sequence
L
LogicLoop

High-quality programming content and resources for developers of all skill levels. Our platform offers comprehensive tutorials, practical code examples, and interactive learning paths designed to help you master modern development concepts.

© 2025 LogicLoop. All rights reserved.