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
{{ message }}
Repository navigation
2.1 Regression: (A | undefined) & B is possibly undefined #12354
so the result is ({ id: number } | undefined) & ({ id: number }) which is { id: number } | undefined
The language does not have the constructs today to accurately model the behavior of Object.assing. Adding a spread like operator on types and using it to define Object.assign is tracked by #11100
Did something change in this regard since 2.0? Because it worked. (I think there was a change related to distributivity of | and & in typing, I swear I saw an issue about that somewhere)
so the result is ({ id: number } | undefined) & ({ id: number }) which is { id: number } | undefined.
Shouldn't the result of (T | undefined) & T be (T & T) | (T & undefined) which is just T.
Blake Embrey (@blakeembrey) interesting but is that required? The result of (A | undefined) & B is not possibly undefined because it must include all members of B.
This is the behavior of 2.0 and it's a regression, which is not a dupe (I'm not asking for perfect typing, which will come with spread types, I know).
jods (@jods4) this behavior is introduced in #11717. the change normalizes union types, so that (A | undefined) & B is treated the same way as (A & B) | (undefined & B).
the fact that it used to work in the past is just a bug that was uncovered by this change.
the correct fix for the issue in the OP is by correctly modeling the behavior or Object.assign.
Mohamed Hegazy (@mhegazy) not sure I understand here.
How is (A & B) | (undefined & B) potentially undefined?
The left side A & B is obviously not.
The right side undefined & Bis notundefined and has to provide all properties of B.
For as much as I can think of, undefined & B is equivalent to just B.
the compiler says if the type is undefined then it is not safe to use. and the type is undefined.
the problem is not which rule to invoke. the type undefined & B is as invalid as number & string. it represents an empty set with no values, and it does not exist at runtime. I do not think there is a right/wrong way to deal with them.
You would not write a type annotation on something to be undefined & B; the real issue here is you got one of these meaningless types from calling Object.assign.
OK I see what you mean.
As Anders pointed out in the initial PR for intersection types, intersection of primitive types don't really work but they can't be prevented in generics instanciations.
It just happened that in 2.0 undefined & B allowed property access because it is a B and now in 2.1 it is forbidden because it is undefined. The statements are contradictory but they are both true, so I agree that none is better than the other.
Surely when the spread types PR lands, Object.assign will return A ... B and the situation will be great. Will it land in 2.1 RTM though?
Otherwise, since this is an annoying breaking change, maybe you should do a little mitigation in the meantime. You could change the assign definition to Object.assign<T, U>(a: T, b: U | null | undefined): T & U as suggested by Blake Embrey (@blakeembrey). That would prevent some breakage until we get spread types.
BTW, I tried to think of other cases where T & null might happen and it seems to me that for many (or even all?) use cases, A ... B replaces A & B. Right now I can't think of one situation in JS where you actually implement A & B, what you do is A ... B.
Activity
mhegazy commentedon Nov 18, 2016
The current declaration of
Object.assignis:so the result is
({ id: number } | undefined) & ({ id: number })which is{ id: number } | undefinedThe language does not have the constructs today to accurately model the behavior of
Object.assing. Adding a spread like operator on types and using it to defineObject.assignis tracked by #11100jods4 commentedon Nov 18, 2016
Did something change in this regard since 2.0? Because it worked.
(I think there was a change related to distributivity of
|and&in typing, I swear I saw an issue about that somewhere)Shouldn't the result of
(T | undefined) & Tbe(T & T) | (T & undefined)which is justT.blakeembrey commentedon Nov 18, 2016
Mohamed Hegazy (@mhegazy) FWIW, I did https://github.com/types/npm-xtend/blob/master/immutable.d.ts#L1 to allow extending
undefined/nullas they have no effect on the final type.jods4 commentedon Nov 18, 2016
Blake Embrey (@blakeembrey) interesting but is that required? The result of
(A | undefined) & Bis not possiblyundefinedbecause it must include all members ofB.This is the behavior of 2.0 and it's a regression, which is not a dupe (I'm not asking for perfect typing, which will come with spread types, I know).
[-]2.1 Regression: Object.assign and undefined[/-][+]2.1 Regression: (A | undefined) & B is possibly undefined[/+]mhegazy commentedon Nov 18, 2016
jods (@jods4) this behavior is introduced in #11717. the change normalizes union types, so that
(A | undefined) & Bis treated the same way as(A & B) | (undefined & B).the fact that it used to work in the past is just a bug that was uncovered by this change.
the correct fix for the issue in the OP is by correctly modeling the behavior or
Object.assign.jods4 commentedon Nov 18, 2016
Mohamed Hegazy (@mhegazy) not sure I understand here.
How is
(A & B) | (undefined & B)potentiallyundefined?The left side
A & Bis obviously not.The right side
undefined & Bis notundefinedand has to provide all properties ofB.For as much as I can think of,
undefined & Bis equivalent to justB.mhegazy commentedon Nov 18, 2016
undefined & Bisundefinedand isB. undefined has a value not likeneverfor instance.jods4 commentedon Nov 18, 2016
As an illustration, assume
type B = { n: number }and someb: undefined & B.From your own words,
undefined & Bisundefinedand isBso there is no reason that I should not be able to dob.n.nhas to exist onb, otherwise it would not beundefined & B.Currently the compiler is complaining that
b.nis not valid and that is incorrect.mhegazy commentedon Nov 18, 2016
the compiler says if the type is
undefinedthen it is not safe to use. and the type isundefined.the problem is not which rule to invoke. the type
undefined & Bis as invalid asnumber & string. it represents an empty set with no values, and it does not exist at runtime. I do not think there is a right/wrong way to deal with them.You would not write a type annotation on something to be
undefined & B; the real issue here is you got one of these meaningless types from callingObject.assign.jods4 commentedon Nov 18, 2016
OK I see what you mean.
As Anders pointed out in the initial PR for intersection types, intersection of primitive types don't really work but they can't be prevented in generics instanciations.
It just happened that in 2.0
undefined & Ballowed property access because it is aBand now in 2.1 it is forbidden because it isundefined. The statements are contradictory but they are both true, so I agree that none is better than the other.Surely when the spread types PR lands,
Object.assignwill returnA ... Band the situation will be great. Will it land in 2.1 RTM though?Otherwise, since this is an annoying breaking change, maybe you should do a little mitigation in the meantime. You could change the
assigndefinition toObject.assign<T, U>(a: T, b: U | null | undefined): T & Uas suggested by Blake Embrey (@blakeembrey). That would prevent some breakage until we get spread types.BTW, I tried to think of other cases where
T & nullmight happen and it seems to me that for many (or even all?) use cases,A ... BreplacesA & B. Right now I can't think of one situation in JS where you actually implementA & B, what you do isA ... B.