Question

Fix “Would Overwrite Input File” in TypeScript

typescriptvisual-studiovisual-studio-2015typescript2.0

Question

In a TypeScript 2.2.1 project using Visual Studio 2015 Update 3, the Error List shows many messages similar to the following:

Cannot write file 'C:/{{my-project}}/node_modules/buffer-shims/index.js' because it would overwrite input file.

The project still builds and runs, but these messages make it difficult to find genuine errors. Given this tsconfig.json configuration, how can these errors be removed?

{
  "compileOnSave": true,
  "compilerOptions": {
    "baseUrl": ".",
    "module": "commonjs",
    "noImplicitAny": true,
    "removeComments": true,
    "sourceMap": true,
    "target": "ES5",
    "forceConsistentCasingInFileNames": true,
    "strictNullChecks": true,
    "allowUnreachableCode": false,
    "allowUnusedLabels": false,
    "noFallthroughCasesInSwitch": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "typeRoots": [],
    "types": []
  },
  "exclude": ["node_modules"]
}

Short Answer

You will learn why TypeScript refuses to emit a file over a file it is already reading, why exclude is not always enough, and how to keep source files, dependencies, and generated JavaScript separate.

Concept

TypeScript has two important jobs:

  1. It builds a program by finding source files and resolving imports.
  2. It emits JavaScript, source maps, and declaration files.

The error appears when TypeScript calculates an output path that is also an input path:

input:  node_modules/package/index.js
output: node_modules/package/index.js

Overwriting an input file during compilation could destroy a dependency or make the compiler's results unreliable, so TypeScript stops and reports the conflict.

This commonly happens when JavaScript files are included as compiler inputs, usually because allowJs is enabled somewhere or because an editor/project integration includes JavaScript files. If there is no outDir, TypeScript normally emits output next to its source files. That is unsafe for .js input files because their emitted JavaScript path is the same path.

exclude helps control automatic root-file discovery, but it is not an absolute firewall. A file can still enter the program through imports, project tooling, or explicit file lists. A reliable setup explicitly includes the application's TypeScript source directory and emits generated files into a separate output directory.

Mental Model

Think of TypeScript as a printing machine.

  • Your source files are the documents fed into the machine.
  • Your node_modules directory is a shelf of reference books.
  • Emitted JavaScript is the printed result.

If the printer is told to print directly onto a reference book that it is currently reading, it refuses. The solution is to print into a dedicated output tray such as dist, while keeping dependencies on their own shelf.

Take Quiz

Syntax and Examples

A typical TypeScript project keeps application code in src and compiled files in dist:

{
  "compilerOptions": {
    "target": "ES5",
    "module": "commonjs",
    "outDir": "./dist",
    "sourceMap": true,
    "allowJs": false
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

Key settings:

  • include: selects the TypeScript files that belong to your application.
  • exclude: prevents directories from being found during automatic file discovery.
  • outDir: puts generated .js and .js.map files in a separate directory.
  • allowJs: false: prevents ordinary JavaScript files from being treated as TypeScript compiler inputs. This is the default, but stating it explicitly can make the intended configuration clear.

For example, with this structure:

project/
  src/
    app.ts
  node_modules/
  dist/
  tsconfig.json

compiling src/app.ts produces dist/app.js, not src/app.js or a file inside node_modules.

Step by Step Execution

Consider this configuration:

{
  "compilerOptions": {
    "target": "ES5",
    "module": "commonjs",
    "outDir": "dist",
    "allowJs": false
  },
  "include": ["src/**/*.ts"]
}

And this source file:

// src/greeting.ts
const message = "Hello, TypeScript";
console.log(message);

When tsc runs:

  1. It reads tsconfig.json.
  2. include finds src/greeting.ts.
  3. TypeScript resolves any imports used by greeting.ts.
  4. It type-checks the code.
  5. Because outDir is dist, it emits dist/greeting.js.
  6. Since allowJs is false, existing .js files in node_modules are not compiled as JavaScript inputs.

The output location differs from the input location, so no overwrite conflict occurs.

Real World Use Cases

Separate input and output directories are useful in nearly every TypeScript application:

  • Node.js services: compile src/server.ts to dist/server.js, then run node dist/server.js.
  • CLI tools: keep source in src, publish only the generated JavaScript and required package files.
  • Frontend applications: let a bundler handle final browser assets while TypeScript writes intermediate output to a build directory, or use noEmit when the bundler performs transpilation.
  • Monorepos: each package can use its own src and dist folders, avoiding output collisions between packages.
  • Libraries: emit JavaScript and .d.ts declaration files to dist without modifying package dependencies.
Take Quiz

Real Codebase Usage

In real projects, developers usually define a clear boundary between authored files and generated files.

A common layout is:

project/
  src/          # TypeScript written by the team
  dist/         # generated JavaScript; usually ignored by Git
  node_modules/ # installed dependencies
  tsconfig.json

A practical configuration might be:

{
  "compilerOptions": {
    "target": "ES5",
    "module": "commonjs",
    "outDir": "dist",
    "rootDir": "src",
    "sourceMap": true,
    "strict": true,
    "allowJs": false
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

Teams also commonly add dist to .gitignore:

node_modules/
dist/

If a project uses Babel, webpack, Vite, or another build tool to transpile TypeScript, TypeScript may be configured with "noEmit": true. In that setup, TypeScript checks types but does not write JavaScript at all.

Common Mistakes

Relying only on exclude

{
  "exclude": ["node_modules"]
}

This is helpful, but it does not guarantee that no dependency file will ever be resolved through an import or added by external tooling. Add a focused include list for your application code.

{
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

Emitting next to source files accidentally

Without outDir, TypeScript usually writes output beside each input .ts file. This can clutter source folders and increases the chance of path conflicts in mixed JavaScript/TypeScript projects.

Use a dedicated output directory:

{
  "compilerOptions": {
    "outDir": "dist"
  }
}

Enabling allowJs without an output plan

{
  "compilerOptions": {
    "allowJs": true
  }
}

allowJs is useful when migrating a JavaScript project gradually. However, if JavaScript files are compiler inputs and output is written beside them, TypeScript can attempt to overwrite them.

Use outDir, restrict include, or disable allowJs if JavaScript compilation is not needed.

Putting generated output inside an input directory

Avoid configurations where dist is inside src unless it is carefully excluded. Otherwise a later build can discover its own generated files.

src/
  app.ts
  dist/       # avoid this layout when possible

Prefer sibling directories:

src/
dist/

Assuming types controls compiled files

types and typeRoots control which ambient type packages are included. They do not determine whether ordinary JavaScript files are compiled or where output is written.

Comparisons

Setting or approachWhat it controlsDoes it prevent output collisions by itself?
excludeFiles skipped during automatic root-file discoveryNo; imports and tooling can still add files
includeFiles intended to be root inputsHelps greatly by limiting the project scope
outDirWhere emitted files are writtenYes, when it is separate from inputs
allowJs: falseWhether .js files are accepted as inputsHelps when the unwanted inputs are JavaScript files
noEmit: trueWhether TypeScript writes output at allYes; no files are emitted

include and exclude answer, “What source files should this project start with?” outDir answers, “Where should generated files go?” They solve related but different problems.

For a normal compiling application, use include plus outDir. For type-checking only, use noEmit.

Take Quiz

Cheat Sheet

{
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "allowJs": false
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}
  • Overwrite input file means an emitted file path matches a compiler input path.
  • Use outDir to separate generated JavaScript from source and dependencies.
  • Use include to define the application's source boundary.
  • Use exclude to avoid automatic discovery of dependency and build directories.
  • exclude does not necessarily block files reached through imports or external project tooling.
  • Set allowJs to false unless you deliberately want TypeScript to process .js files.
  • Use noEmit: true when another tool performs transpilation.

FAQ

Why does TypeScript say it would overwrite an input file?

It is about to write an output file at a path that it is already using as an input. TypeScript blocks the write to avoid replacing source or dependency files.

Why is node_modules involved even though it is excluded?

exclude affects automatic discovery. Dependencies can still be reached during module resolution, or a project/editor integration may include files differently. Restrict include and use a separate outDir.

Does outDir fix this error?

Usually, yes. It gives emitted files a different destination, such as dist, rather than writing beside inputs.

Should I delete node_modules to fix the problem?

No. Deleting and reinstalling dependencies may temporarily change symptoms but does not fix an incorrect input/output configuration.

Should I set allowJs to false?

Yes, if the project is intended to compile TypeScript only. If you are intentionally migrating JavaScript, keep allowJs only with a well-defined include list and outDir.

What does types: [] do?

It prevents automatic inclusion of ambient type packages. It does not prevent JavaScript files from being compiler inputs and does not control emitted file paths.

When should I use noEmit instead of outDir?

Use noEmit when TypeScript should only type-check and another tool creates the JavaScript output. Use outDir when tsc is responsible for generating JavaScript.

Take Quiz

Mini Project

Description

Configure a small Node.js TypeScript project so its source stays in src, installed packages remain untouched in node_modules, and compiled JavaScript is written to dist. This demonstrates the directory separation that prevents overwrite-input errors.

Goal

Compile a TypeScript greeting script to dist/greeting.js without compiling or overwriting dependency files.

Requirements

Create a src/greeting.ts file that prints a greeting. Configure TypeScript to compile only .ts files under src. Write generated JavaScript to a dist directory. Ensure JavaScript files are not accepted as compiler inputs. Exclude node_modules and dist from automatic discovery.

Take Quiz

Keep learning

Related questions

View more