Description
π Search Terms
setter union variance
is:issue in-title: setter (nothing relevant found)
is:issue in-title: setter union variance (nothing relevant found)
π Version & Regression Information
confirmed 4.3.5 ~ 5.3.2
β― Playground Link
π» Code
declare const id:<T>()=>T
function f2(x: {f:((a:1|2)=>1|2)} | {f:((a:2|3)=>2|3)} ){
const r = x.f(id())
// ^ const id: <2>() => 2,
// ^ const r: 1 | 2 | 3
}
function f3(f: ((a:1|2)=>1|2) | ((a:2|3)=>2|3) ){
const r = f(id())
// ^ const id: <2>() => 2,
// ^ const r: 1 | 2 | 3
}
function f4(x: {set d(a:1|2); get r():1|2} | {set d(a:2|3); get r():2|3} ){
// ^ (parameter) x: { d: 1 | 2; readonly r: 1 | 2; } | { d: 2 | 3; readonly r: 2 | 3; }
x.d = id();
// ^ const id: <1 | 2 | 3>() => 1 | 2 | 3
x.r;
// ^ (property) r: 1 | 2 | 3
}
π Actual behavior
function f4(x: {set d(a:1|2); get r():1|2} | {set d(a:2|3); get r():2|3} ){
x.d = id();
// ^ const id: <1 | 2 | 3>() => 1 | 2 | 3
The setter function has range 1|2|3
.
π Expected behavior
function f4(x: {set d(a:1|2); get r():1|2} | {set d(a:2|3); get r():2|3} ){
// ^ (parameter) x: {set d(a:1|2); get r():1|2;} | {set d(a:2|3); get r():2|3;}
x.d = id();
// ^ const id: <2>() => 2
- The setter function has range
2
, because that is the valid range that can be assigned to both setter functions in the union.- Compare to the ranges
2
and2
computed for function parametersa
inf2
andf3
.
- Compare to the ranges
- The type display for
x
should show getters and setters instead of plain (readonly for getter) properties.
Additional information about the issue
-
The parameter range of the union of two functions is the intersection of those two functions parameter ranges. That is because anything outside of that intersection would be invalid input in at least one of the two.
-
Theoretically, writes and reads of plain properties of unions of object could also "legally" have opposite variances, with writes being narrower - but they are not treated that way, which although unsound is ok because type widening is allowed for convenience.
-
For the special cases where user want a setter, or setter-getter same-name pair, to behave write-unsoundly exactly like a plain property, they can specify the type as being a plain type, e.g.
{ d: T }
instead of{ set d(value:T): }
. That's an argument for allowing write-sound behavior by default in a type definition{ set d(value:T): }
. -
The setters are converted to plain properties (and the getters are converted to a readonly plain properties (probably at point of declaration) as can be seen by tool tipping to x which shows
(parameter) x: { d: 1 | 2; readonly r: 1 | 2; } | { d: 2 | 3; readonly r: 2 | 3; }
. Related to Flow is improperly narrowing getter valuesΒ #56899.