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
Narrowing on assignment does not remove weak types #26405
Search Terms: narrow assignment weak type assignable comparable
Code
typeAOrArrA<T>=T|T[];constarr: AOrArrA<{x?: "ok"}>=[{x: "ok"}];// weak typearr.push({x: "ok"});// Error: Property 'push' does not exist on type 'AOrArrA<{ x?: "ok"; }>'.
Narrowed type = intersection of declared and assigned types: Fails with let foo: Foo[] = [], where the narrowed type is never[] even after a push.
Narrowed type = minimal sub-union of declared type to which assigned type is assignable: Picks an arbitrary union constituent when the assigned type is any. Even if we fixed that somehow, let foo: {a: number} | {b: number} = {a: 1, b: 2} would arbitrarily pick one of the two union constituents, an undesirable loss of symmetry.
So I think it's most practical to just fix weak types. To recap, we want to (1) decompose unions on the source side, (2) consider the constraints of indexed access types on the source side and decompose unions again after doing so, and (3) check for weak types. The old implementation did (1) and (3) but not (2); the one from #26143 does (1) and (2) but not (3). ISTM it's going to be awkward to get (2) without building on the existing checkTypeRelatedTo code. Thus, I think the best way we can get (1), (2), and (3) is to add a new relation that is the same as the comparable relation but checks for weak types; I propose to name it the "narrowable" relation. I'll submit a PR to do this. I hesitate to add a new relation, but it's just a matter of replacing each of the ~10 occurrences of relation === comparableRelation with relation === comparableRelation || relation === narrowableRelation except for the one for weak types.
Wait, I have another idea: use the old algorithm but if it messes up and produces a narrowed type to which the assigned type is not assignable (as in #26130), then skip narrowing and use the declared type. I updated #26425.
By the way, another example that confuses the old algorithm (playground):
Activity
RyanCavanaugh commentedon Aug 13, 2018
Anders Hejlsberg (@ahejlsberg) tentatively tagging Bug but we'll figure out what we really want to do
mattmccutchen commentedon Aug 13, 2018
Continuing the work toward a solution from #26143 (comment):
I tried:
let foo: Foo[] = [], where the narrowed type isnever[]even after apush.any. Even if we fixed that somehow,let foo: {a: number} | {b: number} = {a: 1, b: 2}would arbitrarily pick one of the two union constituents, an undesirable loss of symmetry.So I think it's most practical to just fix weak types. To recap, we want to (1) decompose unions on the source side, (2) consider the constraints of indexed access types on the source side and decompose unions again after doing so, and (3) check for weak types. The old implementation did (1) and (3) but not (2); the one from #26143 does (1) and (2) but not (3). ISTM it's going to be awkward to get (2) without building on the existing
checkTypeRelatedTocode. Thus, I think the best way we can get (1), (2), and (3) is to add a new relation that is the same as the comparable relation but checks for weak types; I propose to name it the "narrowable" relation. I'll submit a PR to do this. I hesitate to add a new relation, but it's just a matter of replacing each of the ~10 occurrences ofrelation === comparableRelationwithrelation === comparableRelation || relation === narrowableRelationexcept for the one for weak types.Have getAssignmentReducedType use a new relation that checks for weak
mattmccutchen commentedon Aug 13, 2018
Proposed fix is at #26425.
RyanCavanaugh commentedon Aug 17, 2018
Per #26518, we'll revert the changes in #26143
Go back to the old narrowing algorithm (pre microsoft#26143) and avoid …
mattmccutchen commentedon Aug 18, 2018
Wait, I have another idea: use the old algorithm but if it messes up and produces a narrowed type to which the assigned type is not assignable (as in #26130), then skip narrowing and use the declared type. I updated #26425.
By the way, another example that confuses the old algorithm (playground):