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
The assignment fails in TS4.9+ with Type 'Set<(data: string | number) => void>' is not assignable to type 'Set<(data: string) => void> & Set<(data: number) => void>'. and Types of property 'add' are incompatible.
π Expected behavior
The assignment should presumably succeed as it did in TS 4.8-.
Given that Set<T> is considered to be covariant in T (despite being "morally" invariant due to methods like add()),
and given that (x: T) => void is considered to be contravariant in T,
then Set<(x: T)=>void> & Set<(x: U)=>void> should (probably?) be assignable to Set<(x: T | U) => void>.
It looks like #51140 might be responsible for the change.
This also occurs with Array<T> or any type considered covariant but having bivariant methods; if you remove the bivariant methods from Set then the assignment succeeds.
The text was updated successfully, but these errors were encountered:
This one is interesting. The immediate cause of the change in behavior is #51140. However, that PR simply improved our checking enough to reveal a deeper issue that has existed ever since #15104 was introduced in TS 2.4. In that PR we started checking callback parameters covariantly instead of bivariantly:
typeCovar<T>={setCallback(cb: (value: T)=>void): void}declareletcu: Covar<unknown>;declareletcs: Covar<string>;cu=cs;// Okcs=cu;// Error, but wasn't before #15104
The specific change in #15104 was to detect parameters of single-signature function types and exclude them from the bidirectional checking we otherwise do for parameters of methods. But there's a subtle inconsistency that can arise from this:
Above, Bivar<T> is bivariant in T and we measure it's variance accordingly. That means that two instantiations of Bivar<T> are related as long as the type arguments for T are related in one direction or the other. But, in the second example above, we also have a rule that says methods with parameters of single-signature function types should relate covariantly, and the set method qualifies as such in an instantiation of Bivar<T> where T is a single-signature function type. Above, bivariant checking wins because we trust the measured variance for T, but when relating structurally we get a different outcome:
To fix this we need to slightly amend the callback checking rules such that we only special case parameters of single-signature function types when they have single-signature function types for all possible instantiations of the containing signature. In other words, no special callback checking when the pattern arises due to instantiation of a parameter with a naked generic type.
Bug Report
π Search Terms
intersection, contravariant, bivariant, Array, Set, union, covariant, assignability
π Version & Regression Information
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
The assignment fails in TS4.9+ with
Type 'Set<(data: string | number) => void>' is not assignable to type 'Set<(data: string) => void> & Set<(data: number) => void>'.
andTypes of property 'add' are incompatible.
π Expected behavior
The assignment should presumably succeed as it did in TS 4.8-.
Set<T>
is considered to be covariant inT
(despite being "morally" invariant due to methods likeadd()
),(x: T) => void
is considered to be contravariant inT
,Set<(x: T)=>void> & Set<(x: U)=>void>
should (probably?) be assignable toSet<(x: T | U) => void>
.A Stack Overflow question has run into this.
It looks like #51140 might be responsible for the change.
This also occurs with
Array<T>
or any type considered covariant but having bivariant methods; if you remove the bivariant methods fromSet
then the assignment succeeds.The text was updated successfully, but these errors were encountered: