In TypeScript, when you have intersections of function types, they are treated as function overloads. This means that each additional call signature for the same type (or method declaration in the case mentioned above) acts as an extra overload for that function or method. While this behavior may not be explicitly explained in the documentation, there is a section in the TypeScript specification document that sheds some light on how callable types are handled within intersections:
When an intersection type I has constituent types with call signatures, I itself will have those call signatures in order based on the order of the constituents within I.
This essentially implies that an intersection of functions behaves like a single function with multiple call signatures from the intersecting types. In TypeScript, such a function is referred to as an "overloaded function."
Therefore, having an intersection of functions isn't wrong; it's just a way of specifying that a particular object B
should have methods a
and b
with varying signatures. For instance, consider the following interface:
interface IB {
a(x: number): string;
a(x: string): string;
b(x: number): string;
b(x: number): string;
}
While this might seem odd, especially for method b
, it's still valid and not considered an error.
The reason why declaring A
as an interface and B extends A
doesn't work as expected is because the extends
keyword behaves differently than an intersection. With B extends A
, incompatible properties cannot be declared at all, even for non-function properties. For example, attempting to extend interfaces C {x: number | string}
and
D extends C {x: string | boolean}
will result in an error because
string | boolean
is not compatible with
number | string
. This is unlike the intersection operation
(number | string) & (string | boolean)
, which simply results in
string
.
If you're encountering an error with your b
variable, it's likely due to incorrect return types in your a
and b
methods. All methods in object B
must return a string
, but your provided implementations have different return types.
To resolve this, you can modify your code as follows:
const b: B = {
a: (x: string | number) => String(x),
b: (x: number) => String(x)
} // no errors now
With these adjustments, your code should work correctly now. Best of luck!
Link to code