Extending TypeScript Interfaces and Type Aliases with common properties
In this article, I want to discuss what happens when extending TypeScript Interfaces and intersecting Type Aliases that have common properties of different types.
Extending Interfaces
Let's start with interfaces first. Assume an interface IBase that has an optional property prop_b:
Let's say we want to extend this interface with another interface that adds a new property prop_new but also changes the property prop_b to a required property and prop_a to an optional property:
If you try that, you will see the following TypeScript error:
As the error suggests, this happens because the property prop_a in the IExtendBase interface cannot override the same property in the extended interface IBase. The prop_a in the IExtendBase has a broader set of possible value types than the same property in the IBase interface. By the way, this would also happen if the prop_a property in the IExtendBase wouldn't be optional but had a broader set of value types. For example, having a union of string and number would also throw an error:
To fix this problem, we need to omit the prop_a property from the IBase interface before extending it:
Now the new extended interface will have the properties the way we want them:
- The overridden
prop_ais optional - The overridden
prop_bis required - The original
prop_cis not changed - The new required
prop_newwas added
Extending Type Aliases
Type aliases behave similarly to interfaces. However, if we try to achieve similar behavior with types, we won't get an error message as we did before.
Instead, the prop_a in the TExtendBase type will be required, not optional!
This happens because the intersection (&) of two types creates a new type with all the properties included in the intersected types (union of property names). In contrast, the type of every property is an intersection of its value types in the intersected types (intersection of property types). Moreover, if the prop_a property in the TExtendBase type would have a broader set of value types, they would be intersected with the string type from the TBase type leaving a string type:
Here is a diagram showing how the type alias intersection works:
Interestingly, a naive intersection logic can be achieved using the following type:
The fix
Just like with interfaces, to fix this problem, we need to omit the prop_a property from the TBase type before intersecting it: