Skip to content

Treat intersection types as potential never #47751

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

Closed
wants to merge 3 commits into from
Closed

Conversation

lowr
Copy link
Contributor

@lowr lowr commented Feb 6, 2022

Fixes #46032

This PR fixes crashes and improves error messages when a function is declared to return a type alias that names an intersection type of object-like types that reduces to never.

Besides #46032, there have been certain circumstances under which using such type aliases as functions' return type emits error messages different from using never directly (playground). This PR improves error messages so that they emit the same errors (aside from elaboration of never intersections). This is actually not really related to #46032, so let me know if I should open a separate issue/PR.

When the following conditions are satisfied,
`isSimpleTypeRelatedTo(source, target)` returns true, which is false
positive.

 - `strictNullChecks` is disabled

 - `source` is `undefined`

 - `target` is an empty intersection type which reduces to `never` by
   discriminants

In this case, `isSimpleTypeRelatedTo()` should actually return `false`,
which does *not* mean `source` doesn't relate to `target`, but rather
tells the checker to further investigate their relation.
@typescript-bot typescript-bot added the For Backlog Bug PRs that fix a backlog bug label Feb 6, 2022
lowr added 2 commits February 6, 2022 19:10
When types are checked if they are `never` by their `TypeFlags`, they
must first be reduced, because intersection types of object-like types
which reduce to `never` don't have `TypeFlags.Never`. See microsoft#36696 for
details.

When a function is declared to return such type and it doesn't contain
any return statement or it only contains return statements without
expression, the error messages are incorrect as the checker sees its
return type as non-`never`.

This commit takes into account that intersection types potentially
reduces to `never` and improves error messages.
@@ -17815,6 +17815,8 @@ namespace ts {
const t = target.flags;
if (t & TypeFlags.AnyOrUnknown || s & TypeFlags.Never || source === wildcardType) return true;
if (t & TypeFlags.Never) return false;
// Intersection types that would be reduced to never may be passed as `target`, thus returning early.
if (t & TypeFlags.Intersection) return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't quite the right fix, I will put something up in a PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. Besides my fix not allowing empty array literals assigned to never[], is it not "quite the right fix" because it fails to catch cases where target is a union of empty intersections that also reduce to never? A bit of explanation or even pointers would be appreciated if you don't mind!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, one issue is that both unions and intersections can potentially reduce to never, so both need to be excluded when --strictNullChecks is disabled. But another issue is that any should be assignable to anything, which is handled by a test later in the body of isSimpleTypeRelatedTo. So the code can't just unconditionally return false here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'll think about those logic more carefully next time.

@@ -32138,7 +32140,8 @@ namespace ts {
}

const functionFlags = getFunctionFlags(func);
const type = returnType && unwrapReturnType(returnType, functionFlags);
const unwrappedType = returnType && unwrapReturnType(returnType, functionFlags);
const type = unwrappedType && getReducedType(unwrappedType);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is best covered in a separate PR since it is really an unrelated fix.

@ahejlsberg
Copy link
Member

@lowr Thanks for your work on this issue. My suggested fix is in #47816. Feel free to copy from that into this PR, or we can just merge mine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Backlog Bug PRs that fix a backlog bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Crash trying to assing undefined[] to never[]
3 participants