GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Empty intersection reduction by ahejlsberg · Pull Request #31838 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Jun 9, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

With this PR we reduce empty intersections to never immediately upon construction. For example, instead of preserving the type string & number and only reducing it away in union types we now immediately reduce it to never.

Some examples of types that now immediately reduce to never:

type T1 = 'a' & 'b';  // never
type T2 = { a: string } & null;  // never in strict mode, null in non-strict mode
type T3 = { a: string } & undefined;  // never in strict mode, undefined in non-strict mode
type T4 = string & number;  // never
type T5 = number & object;  // never
type T6 = symbol & string;  // never
type T7 = void & string;  // never

In #18438 we chose to preserve empty intersection types (a) to make it easier to understand how they originated and (b) to partially preserve the ability to "tag" primtive types using enum types (e.g. string & Tag where Tag is an enum). In practice this doesn't seem to outweigh the confusion these types bring about, in particular because there are subtle behavioral differences between never and types like string & number even though they really should be the same. The differences include:

  • never is assignable to anything whereas string & number is assignable only to string or number.
  • any is not assignable to never but is assignable to string & number.
  • For a type T with no index signatures, T[never] resolves to never whereas T[string & number] is an error.

These differences have no practical value. Particularly confusing is that string & boolean reduces to never (because it normalizes to string & true | string & false, which then reduces to never), whereas string & number sticks around and behaves differently.

Fixes #31663.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Anders Hejlsberg (ahejlsberg) added 7 commits June 9, 2019 07:46
@ahejlsberg
Intersections of disjoint types become never upon construction
d4c9f24
@ahejlsberg
Update tests
9cc9fb9
@ahejlsberg
Accept new baselines
d872b3a
@ahejlsberg
Fix minor issue
d2397e0
@ahejlsberg
Update fourslash tests
aa0ea51
@ahejlsberg
Add tests
98bbb22
@ahejlsberg
Accept new baselines
015f4c5
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 9, 2019

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) test this
TypeScript Bot (@typescript-bot) run dt

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 9, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the parallelized Definitely Typed test suite on this PR at 015f4c5. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 9, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the extended test suite on this PR at 015f4c5. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 9, 2019

Copy link
Copy Markdown
Member Author

RWC test run is clean. DT test run has one minor difference in ember/test/detect-instance.ts. Types such as null & { foo: string } and undefined & { foo: string } now reduce to never but previously would stick around (yet disappear in union types) and allow properties to be accessed. The new behavior is definitely more consistent.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jack-williams

Jack Williams (jack-williams) commented Jun 9, 2019

Copy link
Copy Markdown
Collaborator

Does this still support branding via object literals?

type Tag<T> = T & { readonly brand: unique symbol };
type TaggedNumber = Tag<number>; // not never
danvk reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 9, 2019

Copy link
Copy Markdown
Member Author

Jack Williams (@jack-williams) It does, except you can't brand null or undefined. You sort of could before, but only in a limited sense. The minute you put null & { brand: xxx } in a union type it would disappear.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 9, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

Jack Williams (@jack-williams) The only thing that changes with this PR is when we reduce away empty intersections. We now do it upon construction but we used to do it only when an intersection was put in a union type. The actual patterns we reduce away (or don't reduce away) remain the same.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@jack-williams

Jack Williams (jack-williams) commented Jun 9, 2019

Copy link
Copy Markdown
Collaborator

The only thing that changes with this PR is when we reduce away empty intersections.

Ah, that makes it clearer. Thank you!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

weswigham
Wesley Wigham (weswigham) reviewed Jun 10, 2019
View reviewed changes
Comment thread src/compiler/checker.ts
if (type.flags & TypeFlags.Unit && includes & TypeFlags.Unit) {
// We have seen two distinct unit types which means we should reduce to an
// empty intersection. Adding TypeFlags.NonPrimitive causes that to happen.
includes |= TypeFlags.NonPrimitive;

@weswigham Wesley Wigham (weswigham) Jun 10, 2019

Copy link
Copy Markdown
Member

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.

Counterexample:

enum A {
  Zero = 0,
  One = 1
}

type Zeroish<T> = T & 0;
type ShouldNotBeNever = Zeroish<A.Zero> | never;

we handle this incorrectly in master already - I'm just not a fan of propagating the logical error.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@fatcerberus Bruce Pascoe (fatcerberus) Jun 10, 2019

Copy link
Copy Markdown

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.

So is the reason for that that the code highlighted considers A.Zero and 0 to be distinct? I'm just trying to ascertain where in that example above the never creeps in.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Jun 11, 2019

Copy link
Copy Markdown
Member Author

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.

Wesley Wigham (@weswigham) I think it is debatable whether we want to consider literals and literal enum members with the same underlying value to be intersectable. But either way, it is an issue that should be covered in a separate PR. Here we're just concerned with changing when we reduce, not how.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jun 10, 2019
Update Ember tests to treat 'string & number' and 'never' the same DefinitelyTyped/DefinitelyTyped#36095
Merged
@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 11, 2019

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) run dt

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@typescript-bot

TypeScript Bot (typescript-bot) commented Jun 11, 2019 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

Heya Anders Hejlsberg (@ahejlsberg), I've started to run the parallelized Definitely Typed test suite on this PR at 015f4c5. You can monitor the build here. It should now contribute to this PR's status checks.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jun 11, 2019

Copy link
Copy Markdown
Member Author

RWC and DT test runs are both clean now.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg
Anders Hejlsberg (ahejlsberg) requested a review from Wesley Wigham (weswigham) June 11, 2019 17:11
weswigham
Wesley Wigham (weswigham) approved these changes Jun 11, 2019
View reviewed changes

@weswigham Wesley Wigham (weswigham) left a comment

Copy link
Copy Markdown
Member

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.

I expect we'll get some reports about the using-an-enum-to-tag break upon release - we should remember to include this in the breaking change log.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ahejlsberg
Anders Hejlsberg (ahejlsberg) merged commit ad322a5 into master Jun 11, 2019
@ahejlsberg
Anders Hejlsberg (ahejlsberg) deleted the emptyIntersectionReduction branch June 11, 2019 19:25
@ahejlsberg Anders Hejlsberg (ahejlsberg) added the Breaking Change Would introduce errors in existing code label Jun 11, 2019
@ahejlsberg Anders Hejlsberg (ahejlsberg) added this to the TypeScript 3.6.0 milestone Jun 11, 2019
@karol-majewski Karol Majewski (karol-majewski) mentioned this pull request Jun 24, 2019
3.5 regression: invalid type for index lookup using constrained generic #32017
Closed
@jack-williams Jack Williams (jack-williams) mentioned this pull request Aug 29, 2019
error TS2339: Property 'length' does not exist on type 'never' #33138
Closed
@jack-williams Jack Williams (jack-williams) mentioned this pull request Sep 6, 2019
Intersection of non-null and null is not deduced to be never #33274
Closed
@jack-williams Jack Williams (jack-williams) mentioned this pull request Sep 16, 2019
Regression in 3.6: Combination of union and intersection types with literal types not resolving specific types properly #33448
Closed
@jack-williams Jack Williams (jack-williams) mentioned this pull request Sep 28, 2019
Entries cannot be reduced to an interface due to any being assigned to never #33651
Closed
@fatcerberus Bruce Pascoe (fatcerberus) mentioned this pull request Oct 26, 2019
「Bug」multiple candidates for the same type variable in contra-variant positions causes an intersection type to be inferred #34732
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Feb 16, 2020
Reduce intersections by discriminants #36696
Merged
@DanielRosenwasser Daniel Rosenwasser (DanielRosenwasser) mentioned this pull request Feb 19, 2020
Design Meeting Notes, 2/14/2020 #36877
Closed
@deyaaeldeen Deyaa (deyaaeldeen) mentioned this pull request Oct 23, 2020
deserialize null faithfully Azure/azure-sdk-for-js#12009
Closed
@wdamien Wes Gorgichuk (wdamien) mentioned this pull request May 26, 2022
Intersection Types that have overlapping properties can fail #49264
Closed
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Mar 3, 2023
Impossible intersection is supposedly never but any can still be assigned to it #53027
Open
@dharnil

dharnil (dharnil) commented May 31, 2024

Copy link
Copy Markdown

Hi I wanted to check to see if this has already been resolved as I am still facing this issue.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Swiftwork Erik Hughes (Swiftwork) mentioned this pull request Mar 19, 2025
feat: simplify types treating key and payload always as optional reflagcom/javascript#380
Merged
Erik Hughes (Swiftwork) added a commit to reflagcom/javascript that referenced this pull request Mar 20, 2025
@Swiftwork
feat: simplify types treating key and payload always as optional (#380)
2fa4704
Utilizing [TypeScript
intersection](microsoft/TypeScript#31838) to
simplify config types. Config keys and payload can now always be
`undefined`.
@microsoft Microsoft (microsoft) locked as resolved and limited conversation to collaborators Oct 16, 2025
@typescript-bot TypeScript Bot (typescript-bot) assigned Anders Hejlsberg (ahejlsberg) Oct 16, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

@weswigham Wesley Wigham (weswigham) weswigham approved these changes
+1 more reviewer
@fatcerberus Bruce Pascoe (fatcerberus) fatcerberus left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

@ahejlsberg Anders Hejlsberg (ahejlsberg)

Labels

Breaking Change Would introduce errors in existing code

Projects

None yet

Milestone

TypeScript 3.6.0

Development

Successfully merging this pull request may close these issues.

Type 'any' is not assignable to type 'never' with boolean in interface in TS 3.5

Uh oh!

There was an error while loading. Please reload this page.

6 participants

@ahejlsberg @typescript-bot @jack-williams @dharnil @weswigham @fatcerberus
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.