TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
159: Non-Modules and Script Files
A few years ago, I was helping a junior dev migrate a legacy project to TypeScript. He had created two separate files: analytics.ts and app.ts. In both files, he'd defined a simple constant called apiEndpoint. He wasn't importing one into the other; they were just two files in the same folder. When he ran the compiler, he was baffled to see a wall of red errors screaming "Duplicate identifier 'apiEndpoint'". He kept insisting, "But they're different files! How does the compiler even know about the other one?"
What he had stumbled into is one of the most confusing quirks for people moving from a "module-first" mindset: the difference between a TypeScript module and a script file.
The Global Scope Trap
In TypeScript, any file that does not contain an import or export statement is treated as a script file, not a module. This means everything you declare at the top level of that file—variables, functions, classes—is dumped into the global namespace.
If you have ten different .ts files and none of them use import or export, they all share the same global space. It's essentially like putting one giant <script> tag after another in an old HTML page. If you define const user = 'Alice' in fileA.ts and const user = 'Bob' in fileB.ts, TypeScript will throw a compile-time error because it thinks you're trying to declare the same variable twice in the same global environment.
// fileA.ts
const appVersion = "1.0.0"; // Global scope
// fileB.ts
const appVersion = "2.0.0"; // Error: Duplicate identifier 'appVersion'
I'll be honest: this behavior feels archaic. But it exists to maintain compatibility with the way JavaScript worked for decades before ES modules became the standard.
Forcing Module Behavior
The fix is usually simpler than you'd expect. To tell TypeScript, "Treat this file as its own isolated module, not a global script," you just need to add an export statement. Even if you don't actually have anything specific to export, you can use an empty export. This is a common "hack" in the TS community.
By adding export {} to the bottom of your file, you're signaling to the compiler that the file is a module. Suddenly, those variables are scoped to the file itself, and the duplicate identifier errors vanish.
// fileA.ts
const appVersion = "1.0.0";
export {}; // Now this file is a module!
// fileB.ts
const appVersion = "2.0.0";
export {}; // No more conflict with fileA.ts
Of course, the "correct" way to handle this in a modern project is to actually export the things you need and import them where they're used. But when you're dealing with small utility scripts or legacy migrations, export {} is a lifesaver that saves you from having to rename every single variable just to satisfy the compiler.
📋 Practical Task
Fixing Global Namespace Clashes in Session Management
You are working on a project with two files that are currently treated as scripts. Because they both use common naming conventions, the compiler is failing. Your goal is to isolate these files so they no longer clash, without changing the variable names.
File 1: auth-setup.ts
const sessionTimeout = 3600;
function initializeSession() {
console.log("Session started with timeout: " + sessionTimeout);
}
initializeSession();
File 2: guest-setup.ts
const sessionTimeout = 0;
function initializeSession() {
console.log("Guest session started.");
}
initializeSession();
Your Task: Modify both files so that the "Duplicate identifier" errors for sessionTimeout and initializeSession disappear, while ensuring the files remain independent and do not import/export logic from one another.
There are no comments for now.