You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
interfaceFn<A,B>{(a: A): B;then<C>(next: Fn<B,C>): Fn<A,C>;}declareconstfn: Fn<string,number>;constcontravariantInA: 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"'.constcovariantInB: Fn<string,unknown>=fn;// okay
Expected 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-williamssuggests 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?
Related Issues: #1394: let us mark the parameters as covariant/contravariant/invariant ourselves #32674: a different variance issue #33872: a different variance bug
The text was updated successfully, but these errors were encountered:
First off, looks like we measure Fn<A, B> as invariant in A and covariant in B:
declareconstfn: Fn<string,number>;// Invariant in Aconstfn1: Fn<unknown,number>=fn;// Errorconstfn2: Fn<'a',number>=fn;// Error// Covariant in Bconstfn3: Fn<string,unknown>=fn;// Okconstfn4: Fn<string,0>=fn;// Error
This is pretty much the expected result. As we attempt to relate the recursive references to Fn in the then method, the covariant default kicks in. The combination of that and the measured contravariance from the reference to A in the call signature gives us invariance for A. Would be nice to do better, but if we completely remove the default-to-covariance recursion guard and structurally compare until the entire variance measurement is complete, we have repro examples that never terminate. So, we'd need something smarter, like perhaps a depth based mechanism that only activates the recursion guard after a few levels.
We could consider variance annotations, but we would then also need logic to verify that type declarations don't violate them.
Oops, I got my variance backwards in the example code... I'll update it
Edit: all right, I updated it.
jcalz
changed the title
Function-like recursive generic interface invariant in both type parameters
Function-like recursive generic interface invariant in its parameter type
Jan 14, 2020
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
Expected behavior:
Fn<A, B>
should be contravariant inA
.Actual behavior:
Fn<A, B>
is invariant inA
.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 thethen()
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 (intypeArgumentsRelatedTo()
)... 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 onFn<B, C>
which depends onFn<C, D>
which depends onFn<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
The text was updated successfully, but these errors were encountered: