-
Notifications
You must be signed in to change notification settings - Fork 12.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Attempt to fix #9919 - Intersecting discriminated union breaks type narrowing #9939
Conversation
Hi @gcnew, I'm your friendly neighborhood Microsoft Pull Request Bot (You can call me MSBOT). Thanks for your contribution! TTYL, MSBOT; |
@ahejlsberg want to take a look? |
@gcnew, Thanks for signing the contribution license agreement so quickly! Actual humans will now validate the agreement and then evaluate the PR. |
@gcnew Thanks for your contribution! We're about to merge #9407 which contains a number of changes in this area, including optimizations to improve performance in code bases with large union types (100+ constituent types) such as would result if we convert the compiler itself to depend on these features. Merging that will undoubtedly affect this PR. Not sure if you've looked at that already. One concern I have with incorporating intersection types is that I will cause even more types to be generated by narrowing operations, thus negatively affecting performance. Also, if we go beyond just the top level of union types we really should handle any arbitrary number of nested unions and intersections, e.g. |
@@ -8095,7 +8106,7 @@ namespace ts { | |||
} | |||
const propName = propAccess.name.text; | |||
const propType = getTypeOfPropertyOfType(type, propName); | |||
if (!propType || !isStringLiteralUnionType(propType)) { | |||
if (!propType || !(isStringLiteralUnionType(propType) || (type.flags & TypeFlags.UnionOrIntersection))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't want to narrow if the original type was a union? That doesn't sound right.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, maybe the better way to write it is:
if (!propType || (!isStringLiteralUnionType(propType) && (type.flags & TypeFlags.UnionOrIntersection) === 0))
i.e. if neither the discrimination property is a string-literal-union, nor the parent type is a union or intersection. I guess I made it unintelligible by applying De Morgan's law.
@ahejlsberg Thank you for taking a look at this PR! Yes, I'm following #9407 and I suspect that this code will not be compatible with what's to come. I agree that complete support of arbitrary ADT/intersection blending will have memory and performance implications especially if done naively as I did here. On the other hand, features like this one make TypeScript more expressive than ML-like languages. If we can isolate the performance hit to affect only the rare users/use cases that consciously take advantage of this functionality will that be an acceptable compromise? After all having type casts and vanished type-safety is arguably worse. |
This behaviour is being implemented in #11717. Closing this one in favour of the latter. |
Fixes #9919.
This PR adds support for correctly narrowing discriminated unions that are also intersected. As a side effect a type is now eligible for discrimination whenever it is a union or intersection, regardless whether the discrimination property is a union itself. This is to handle situations where the property type is narrowed by subsequent intersections.