This error occurs when you enable TypeScript's 'allowImportingTsExtensions' option without also enabling 'noEmit' or 'emitDeclarationOnly'. The fix depends on whether you're using a bundler or TypeScript runtime.
TypeScript's `allowImportingTsExtensions` option allows importing files with TypeScript-specific extensions like `.ts`, `.mts`, or `.tsx` directly in your source code. However, TypeScript enforces that this option can only be used when `noEmit` or `emitDeclarationOnly` is enabled. This restriction exists because: 1. **Import paths aren't rewritten**: When TypeScript emits JavaScript, it doesn't automatically rewrite `.ts` extensions to `.js` 2. **Runtime incompatibility**: If your compiled JavaScript output contains `import "./file.ts"`, that import will fail at runtime because Node.js, browsers, and other JavaScript runtimes cannot execute `.ts` files directly 3. **Prevents invalid output**: The restriction prevents you from accidentally generating unrunnable JavaScript code The error typically appears when using modern TypeScript setup with a bundler (Webpack, Vite, esbuild) or a TypeScript runtime (tsx, ts-node, Deno, Bun) without proper configuration.
First, determine what you're actually using to run/build your code:
If you're using a bundler (Webpack, Vite, esbuild, Parcel):
Your bundler handles TypeScript transpilation, not TypeScript itself.
If you're using a TypeScript runtime (tsx, ts-node, Deno, Bun):
These runtimes execute TypeScript files directly without emitting JavaScript.
If you're using pure TypeScript compilation (tsc):
TypeScript itself emits the JavaScript files.
Understanding this helps determine the right fix.
Add or update the noEmit compiler option in your tsconfig.json:
{
"compilerOptions": {
"allowImportingTsExtensions": true,
"noEmit": true
}
}noEmit: true tells TypeScript to skip emitting JavaScript entirely. This is appropriate when:
- You're using a bundler to handle transpilation
- You're using a TypeScript runtime (tsx, ts-node, Deno, Bun)
- You only want TypeScript for type-checking
If you need to emit declaration files (.d.ts) but not JavaScript, use:
{
"compilerOptions": {
"allowImportingTsExtensions": true,
"emitDeclarationOnly": true
}
}This is useful for:
- Publishing a library with TypeScript definitions
- Pre-generating type definitions for your bundled code
- Type-only outputs
If you actually need to emit JavaScript files AND use .ts extensions in imports, use rewriteRelativeImportExtensions (TypeScript 5.7+):
{
"compilerOptions": {
"allowImportingTsExtensions": true,
"rewriteRelativeImportExtensions": true,
"module": "esnext",
"moduleResolution": "bundler"
}
}This tells TypeScript to automatically rewrite .ts imports to .js in the emitted output, making the generated JavaScript valid for runtime execution.
Requirements:
- TypeScript 5.7+
- Must use "esnext" or compatible module format
- Use "bundler" or "node" for moduleResolution
If using a bundler, ensure it's configured to handle TypeScript:
Vite:
// vite.config.ts
export default {
esbuild: {
target: 'esnext'
}
}Webpack:
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.ts$/,
use: 'ts-loader'
}
]
}
}esbuild:
import * as esbuild from 'esbuild';
await esbuild.build({
entryPoints: ['src/index.ts'],
outdir: 'dist',
format: 'esm'
});Once configured, test your build:
# With noEmit enabled (type-checking only)
npx tsc --noEmit
# Or run your bundler/runtime
npm run dev
npm run build
tsx src/index.tsThe error should disappear. Your .ts imports should now work correctly.
### TypeScript 6.0 Preview
TypeScript 6.0 (planned future release) will make allowImportingTsExtensions the default behavior, removing the need for explicit configuration. This change recognizes that most modern TypeScript projects use bundlers or runtimes that handle TypeScript natively.
### Using with Different TypeScript Runtimes
tsx:
# No tsconfig.json changes needed if using tsx
tsx src/index.tsts-node:
// tsconfig.json
{
"compilerOptions": {
"noEmit": false,
"allowImportingTsExtensions": true,
"esModuleInterop": true
},
"ts-node": {
"esm": true
}
}Deno:
# Deno handles this automatically
deno run --allow-all src/index.tsBun:
# Bun handles this automatically
bun src/index.ts### When NOT to Use allowImportingTsExtensions
If you're building a library that will be consumed by JavaScript users, avoid .ts extensions in your public API. Stick to .js or standard package exports. Only use .ts extensions in:
- Internal/private code
- Type-only imports
- Tools that will be run with a TypeScript runtime
### CJS vs ESM Considerations
The error is more common in ESM projects. If you're using CommonJS, the configuration differs:
{
"compilerOptions": {
"allowImportingTsExtensions": true,
"noEmit": true,
"module": "commonjs",
"moduleResolution": "node"
}
}Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript