1. The Symptom and Initial Diagnosis
The error log was explicit: 'Duplicate identifier AppConfig'. It triggered during a routine refactor where I was consolidating several configuration objects into a single file. Initially, I suspected that the same variable name was exported twice from different entry points.
I spent an hour cross-referencing imports, only to find the structure was logically sound. It turned out that one file used an interface and another used a type alias, both intending to extend a global configuration namespace.
- Check console logs for duplicate declaration warnings.
- Identify if definitions are using 'interface' or 'type'.
- Verify if any files are unintentionally global-scoped.
2. Uncovering the Declaration Merging Limitation
Once I isolated the configuration files, I realized that interfaces in TypeScript naturally support declaration merging. When I defined two interfaces with the same name, the compiler silently merged them into one, which was exactly what I wanted for extensibility.
However, once I switched to type aliases, the compiler threw a hard error because type aliases are immutable. They do not allow for the same additive behavior as interfaces, leading to the collision I saw in the build logs.
- Interfaces automatically merge if they share the same name.
- Type aliases do not permit re-declaration in the same scope.
- Declaration merging is safer for plugin-based architectures.
3. Why Types Are More Flexible But Rigid
I previously believed these two were interchangeable. While an interface behaves like an object literal, a type alias can define primitives, unions, and complex intersection types. This makes types much more powerful for mapping logic.
The trade-off is the lack of open-endedness. If you need to add properties to a structure later without modifying the original definition, you are locked into interfaces.
- Use 'type' for complex unions and intersection mapping.
- Use 'interface' when you expect the need for declaration merging.
- Refactor to 'type' only when structural immutability is required.
4. Resolving the Build Conflict
To fix the immediate build crash, I standardized our configuration module to use a single interface with optional properties. This removed the need for separate declaration blocks and resolved the 'Duplicate identifier' error instantly.
For non-configuration objects, I kept the type aliases because they strictly enforce the schema I defined. This clarity helped prevent future team members from accidentally extending models that should remain constant.
- Consolidated merged definitions into a single interface.
- Converted static domain objects to explicit type aliases.
- Ran a clean build to verify the elimination of warnings.
FAQ
Can an interface extend a type alias?
Yes, an interface can extend a type alias as long as the type alias references a valid object structure. This is a common pattern for mixing strict types with open interfaces.
Is performance different between types and interfaces?
For most applications, the performance difference is negligible. Focus on readability and the functional requirement of declaration merging rather than micro-benchmarking.