Repository navigation
setter inferred from union has incorrect variance #56894
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 | 3The 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
2and2computed for function parametersainf2andf3.
- Compare to the ranges
- The type display for
xshould 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.
Activity
Andarist commentedon Dec 29, 2023
The issue title should mention setters here :p I prepared a fix for this one: #56895
I also have found that variance is actually computed incorrectly (or rather... it's not observable at all):
The problem is that
isPropertySymbolTypeRelateddoesn't account for write types at all. Perhaps this deserves a new issue?fatcerberus commentedon Dec 29, 2023
They actually do, in certain cases. See e.g. #30769.
[-]getter inferred from union has incorrect variance[/-][+]setter inferred from union has incorrect variance[/+]craigphicks commentedon Dec 29, 2023
Mateusz Burzyński (@Andarist)
Please help me. I don't understand why
inoroutare associated with types rather than property names, howoutdiffers fromreadonly, and howindiffers fromset <property name>(value): void.craigphicks commentedon Dec 29, 2023
I am re-opening this issue because because it seems to have been incorrectly closed without any resolution attributed.
craigphicks commentedon Dec 29, 2023
Bruce Pascoe (@fatcerberus)
Great reference. However, I am puzzled by this test case:
I can't see the reasoning for the errors. This is runtime-error-free node JS:
Andarist commentedon Dec 29, 2023
It likely prevents invalid writes in situations like this:
craigphicks commentedon Dec 29, 2023
Another function format exists to get that same error behavior:
What function should the user call when they don't want those errors?
I think it would be better to trust that the user is reading the function declaration and understands what it means.
craigphicks commentedon Jan 2, 2024
Closing as this is superseded by #56922