-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
I'm aware that this is likely to be a design limitation; I can't find an existing issue about this specific problem so I'm filing this one for reference.
TypeScript Version: 3.8.0-dev.20191220
Search Terms: variance, composition
Code
interface Fn<A, B> {
(a: A): B;
then<C>(next: Fn<B, C>): Fn<A, C>;
}
declare const fn: Fn<string, number>;
const contravariantInA: Fn<"a", number> = fn; // error!
// Type 'Fn<string, number>' is not assignable to type 'Fn<"a", number>'.
// Type 'string' is not assignable to type '"a"'.
const covariantInB: Fn<string, unknown> = fn; // okayExpected behavior: Fn<A, B> should be contravariant in A.
Actual behavior: Fn<A, B> is invariant in A.
I came upon this while investigating a Stack Overflow question in which someone had built a composable type like Fn<A, B> and had to use a workaround to allow the then() method to accept something with a wider input.
@jack-williams suggests that the issue happens because the checker defaults to covariance for A when checking it (in typeArgumentsRelatedTo())... and combined with its contravariant use in the call signature, this results in invariance.
A full structural check might not have this problem but I imagine it might be hard to guarantee that such a check would terminate? (e.g., Fn<A, B> depends on Fn<B, C> which depends on Fn<C, D> which depends on Fn<D, E> etc?)
If there's no plausible way for the compiler to correctly infer the variance here, perhaps this is just another request for variance annotations as per #1394?
Not sure. Anyway, thanks!
Playground Link: Provided
Related Issues:
#1394: let us mark the parameters as covariant/contravariant/invariant ourselves
#32674: a different variance issue
#33872: a different variance bug