The issue with the compiler arises from a concept known as contravariance, where the sub-typing relationship reverses for function inputs.
Consider a scenario where a "plastic bottle" is a subtype of "garbage". What happens when you compare bins that specifically only accept plastic bottles to those that can accommodate any type of garbage?
In this context, a "general garbage bin" acts as a subtype of a "plastic bottle bin". If you place a plastic bottle bin where people are expecting a general garbage bin, individuals may end up disposing of other types of trash that the plastic bottle bin cannot handle.
10
is considered a subtype of number
. This implies that (num: number) => void
is also a subtype of (ten: 10) => void
Therefore, in your second example, while b = a
compiles without errors, a = b
does not since B
is not a subtype of A
. This particular behavior was introduced in 2017 and can be enabled using the --strictFunctionTypes
flag. Disabling this flag would allow both examples to compile successfully.
Why then does the first example compile when acquiring function types via indexed access?
According to the documentation regarding this flag:
During the development phase of this feature, various potentially unsafe class hierarchies, including some within the DOM, were identified. As a result, this setting exclusively applies to functions defined in function syntax and not those written in method syntax.
If needed, we can convert the method into a property featuring a function type:
type A = {
_: (num: number) => void;
}['_'];
type B = {
_: (ten: 10) => void;
}['_'];
Following this transformation, attempting a = b
will fail to compile similar to the outcome observed in your second example.