Question
Fix TypeScript TS1149 File Name Differs Only in Casing
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
./Stateand 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/includeand 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 can refer to the same file. A case-sensitive environment can instead treat them as different names or fail to find one of them.
Mental Model
Think of a TypeScript file as a house with one official street address.
C:/Project/frontend/scripts/State.tsis the full address.../frontend/scripts/State.tsis 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.
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 setting that helps catch this issue is:
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:
- It starts from an entry file or files matched by
include. - It reads
screen.tsand resolves./Statetosrc/State.ts. - It reads
logger.tsand resolves./state. - On a case-insensitive file system,
./statecan still lead to the physicalState.tsfile. - TypeScript notices that the same file is being represented by names with different casing and reports TS1149.
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 isState.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.
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"]
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
}
Comparisons
| Situation | Is it normally valid? | Key rule |
|---|---|---|
Relative import such as ../scripts/State | Yes | It must resolve to the intended file with exact casing. |
| Absolute resolved path shown in an error | Yes | TypeScript commonly converts imports to absolute paths internally. |
./State and ./state for State.ts | No | Use the on-disk filename casing everywhere. |
| Path alias and relative import to different modules | Yes | Configure both correctly and use a clear convention. |
| Path alias and relative import to the same module | Risky | Prefer one canonical import route. |
| Turning off casing checks | Possible but discouraged | It can allow bugs that fail on another machine or CI. |
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, andSTATE. - 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 extensions?
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.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.