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_a
is optional - The overridden
prop_b
is required - The original
prop_c
is not changed - The new required
prop_new
was 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: