Question

Fix TypeScript TS1149 File Name Differs Only in Casing

typescriptjavascriptwindowscompiler-errors

Question

How can I resolve TypeScript error TS1149 when TypeScript reports that these file names differ only in casing?

Error TS1149: File name 'C:/Project/frontend/scripts/State.ts' differs from already included file name '../frontend/scripts/State.ts' only in casing.

All import references and the actual file name appear to use the correct casing. Is the problem caused by a relative path, an incorrectly cased path elsewhere in the build, or by TypeScript including the same file through two different paths?

The project compiles on macOS and Linux but reports this error on Windows. forceConsistentCasingInFileNames is enabled in tsconfig.json, and the build uses tsify.

Short Answer

TS1149 means TypeScript believes one source file has entered the compilation more than once under path names that are not consistently identical. The practical fix is to make every import and build entry resolve the file through one canonical path, with exactly matching capitalization, and to remove duplicate project roots, aliases, or explicit file entries that include it differently.

Concept

TypeScript identifies source files by their resolved file-system paths. A module can be written with a relative import such as:

import { createState } from "../scripts/State";

During compilation, TypeScript resolves that import to a real location, such as:

C:/Project/frontend/scripts/State.ts

A relative import is not inherently a problem. Every relative import eventually resolves to an absolute path internally.

TS1149 appears when the same physical file is discovered through inconsistent path identities. Usually this means one of the following happened:

  • One import says ./State and another says ./state.
  • A path alias and a relative import reach the same file through different project layouts.
  • The file is included by both files/include and a separate build-tool entry point using a differently spelled path.
  • A symlink, linked package, generated configuration, or bundler plugin exposes the same directory through more than one path.
  • The project is invoked from different roots, causing one path to be absolute and another to be relative.

forceConsistentCasingInFileNames asks TypeScript to detect inconsistent casing before it becomes a cross-platform problem. This matters because case sensitivity differs between file systems. Windows is commonly case-insensitive, meaning State.ts and state.ts can refer to the same file. A case-sensitive environment can instead treat them as different names or fail to find one of them.

The message can look confusing when it shows an absolute path on one side and a relative path on the other. The important point is not that one path is relative: it is that the compiler has included what it considers to be the same file through more than one route, and casing or canonical path resolution is inconsistent.

Mental Model

Think of a TypeScript file as a house with one official street address.

  • C:/Project/frontend/scripts/State.ts is the full address.
  • ../frontend/scripts/State.ts is directions from your current location.

Both can lead to the same house, so relative paths are normal. But if some directions call the street State and others call it state, a case-insensitive map may still find the house while TypeScript warns that the addressing system is inconsistent.

Give each file one canonical name and ensure every route used by imports, aliases, build configuration, and tooling leads to it consistently.

Take Quiz

Syntax and Examples

A normal relative import uses the exact on-disk filename casing.

frontend/
└── scripts/
    ├── State.ts
    └── app.ts
// app.ts
import { createState } from "./State";

const state = createState();
console.log(state);

The extension is usually omitted because TypeScript resolves .ts files automatically. The important part is that State matches State.ts exactly.

This is inconsistent and can trigger TS1149 when both imports appear somewhere in the program:

import { createState } from "./State";
import { createState as createOtherState } from "./state"; // Incorrect casing

Fix it by choosing the real filename's spelling everywhere:

import { createState } from "./State";

A tsconfig.json setting that helps catch this issue is:

{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": true
  }
}

Step by Step Execution

Consider this project:

project/
└── src/
    ├── State.ts
    ├── screen.ts
    └── logger.ts
// State.ts
export const mode = "ready";

// screen.ts
import { mode } from "./State";
console.log(mode);

// logger.ts
import { mode } from "./state"; // Incorrect: file is State.ts
console.log(mode);

When TypeScript builds the program:

  1. It starts from an entry file or files matched by include.
  2. It reads screen.ts and resolves ./State to src/State.ts.
  3. It reads logger.ts and resolves ./state.
  4. On a case-insensitive file system, ./state can still lead to the physical State.ts file.
  5. TypeScript notices that the same file is being represented by names with different casing and reports TS1149.
  6. Change ./state to ./State, then rebuild.

If neither visible import has incorrect casing, perform the same trace for all entry points: TypeScript may be receiving one path from tsconfig.json and another from tsify, a bundler, an alias, or a linked dependency.

Real World Use Cases

Consistent module paths prevent failures in several common situations:

  • CI builds: A Windows developer may accidentally write ./state, while a Linux CI runner cannot resolve it when the file is State.ts.
  • Git repositories: Git and case-insensitive file systems can make a case-only rename difficult to notice or apply correctly.
  • Bundled web apps: Bundlers collect modules from many imports. A duplicate path can cause duplicate module instances or compiler errors.
  • Shared component libraries: Consumers should import a component using the public path with the exact exported capitalization.
  • Monorepos: Workspace aliases, package links, and source imports can accidentally load the same source file from different locations.
  • Generated code: A generator that emits import paths must preserve the actual filename casing.
Take Quiz

Real Codebase Usage

In production projects, teams prevent TS1149 by establishing a canonical import strategy.

Use one import style per boundary

For files within a feature, relative imports are often clear:

import { validateUser } from "./validateUser";

For imports across distant folders, projects may use aliases:

import { validateUser } from "@/users/validateUser";

Avoid mixing an alias and a relative path to import the same module in different places unless the tooling is configured to resolve both to one identity.

Keep TypeScript project inclusion simple

A typical configuration includes a single source root:

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

Do not add the same source files again through a separate files list unless there is a specific reason.

Inspect how TypeScript found a file

Use TypeScript's tracing when the problematic import is not obvious:

npx tsc --noEmit --traceResolution

Also list every file included in the compilation:

npx tsc --noEmit --listFiles

Search the repository for every spelling of the module name. For example, search for both State and state. Check bundler entry files and tsify configuration too, because they can provide entry paths outside normal TypeScript imports.

Handle a case-only rename carefully

On Windows, rename through an intermediate name so Git records the change reliably:

git mv State.ts State-temp.ts
git mv State-temp.ts state.ts
git commit -m "Rename State module to lowercase"

Then update every import to ./state. The same process works when renaming from lowercase to uppercase.

Common Mistakes

Assuming relative paths are invalid

This is valid:

import { mode } from "../scripts/State";

A relative path only describes the module from the importing file's directory. The issue is inconsistent resolution, not relativity itself.

Checking only the import currently being edited

The conflicting path can come from another file, a test, a generated file, a barrel export, or a bundler entry. Search the entire repository, including configuration files.

Mixing casing in barrel exports

// Incorrect if the filename is State.ts
export * from "./state";

Use:

export * from "./State";

Disabling forceConsistentCasingInFileNames

{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": false
  }
}

This hides a portability problem rather than fixing it. Keep the option enabled and correct the paths.

Using two routes to one module

import { mode } from "@/scripts/State";
import { mode as duplicateMode } from "../../scripts/State";

Even with matching case, importing the same module through multiple configured routes can complicate tooling and module identity. Prefer one project convention.

Forgetting to restart long-running tools

Watch mode, editor language servers, and bundlers can retain an old module graph after renames. After correcting names, stop and restart the development process. If needed, remove build output or tool caches that are safe to regenerate.

Comparisons

SituationIs it normally valid?Key rule
Relative import such as ../scripts/StateYesIt must resolve to the intended file with exact casing.
Absolute resolved path shown in an errorYesTypeScript commonly converts imports to absolute paths internally.
./State and ./state for State.tsNoUse the on-disk filename casing everywhere.
Path alias and relative import to different modulesYesConfigure both correctly and use a clear convention.
Path alias and relative import to the same moduleRiskyPrefer one canonical import route.
Turning off casing checksPossible but discouragedIt can allow bugs that fail on another machine or CI.

forceConsistentCasingInFileNames is a validation setting. It does not rename files or repair imports; it reports inconsistent file naming so you can fix the source or build configuration.

Take Quiz

Cheat Sheet

  • TS1149: TypeScript found what appears to be the same file under inconsistent path casing.
  • A relative path is normal; it is not automatically the cause.
  • Match imports to the filename exactly: State.ts → import "./State".
  • Search for every spelling, such as State, state, and STATE.
  • Inspect import, export ... from, dynamic imports, test files, generated files, aliases, and bundler entry points.
  • Keep this enabled:
{
  "compilerOptions": {
    "forceConsistentCasingInFileNames": true
  }
}
  • Diagnose resolution:
npx tsc --noEmit --traceResolution
npx tsc --noEmit --listFiles
  • For a case-only Git rename on Windows, rename via a temporary filename.
  • Restart watchers, editors, and bundlers after renaming files or changing module paths.

FAQ

Why does TS1149 mention an absolute path and a relative path?

TypeScript resolves imports to file-system locations internally. One displayed path may be the resolved absolute location while another reflects how the file was first included. The relative path itself is not necessarily wrong.

Does Windows cause TS1149?

Windows commonly uses case-insensitive file systems, which can allow differently cased names to reach the same physical file. TypeScript reports the inconsistency to keep the project portable. The underlying issue is inconsistent naming or inclusion, not Windows alone.

Why might the error appear only on one operating system?

File-system case behavior, Git checkout behavior, the command used to start the build, and tool configuration can differ between machines. A build may also be compiling a different set of entry points on each system.

Can I fix TS1149 by disabling forceConsistentCasingInFileNames?

You can suppress this check, but it is not a reliable fix. Correct the filename casing and module paths so the code behaves consistently across environments.

How do I find the import with the wrong casing?

Search the whole repository for case variants of the module name, then run npx tsc --noEmit --traceResolution to see where TypeScript resolves each import. Check tsify entry points and aliases as well.

Can path aliases contribute to this error?

Yes. An alias and a relative path can expose the same file through separate routes, especially when baseUrl, paths, workspace links, or bundler resolution are misaligned.

Should TypeScript imports include .ts extensions?

In standard TypeScript source imports, extensions are usually omitted. Whether an extension is used is separate from TS1149; the filename portion must still use consistent casing.

Take Quiz

Mini Project

Description

Create a small TypeScript module graph that uses one consistently named State.ts module. The exercise demonstrates how a barrel export and two consuming files must all use the same filename casing to avoid cross-platform build problems.

Goal

Build and run a program that imports a shared state module through consistently cased paths.

Requirements

Create a State.ts file that exports a state object and an update function.|Create an index.ts barrel file that re-exports the state module with the exact filename casing.|Import the module from two application files without using differently cased paths.|Enable forceConsistentCasingInFileNames in tsconfig.json.

Take Quiz

Keep learning

Related questions

View more