Skip to content
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

The behavior of the intersection type of array changed in 2.6.1 #20001

Closed
icepeng opened this issue Nov 14, 2017 · 3 comments
Closed

The behavior of the intersection type of array changed in 2.6.1 #20001

icepeng opened this issue Nov 14, 2017 · 3 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@icepeng
Copy link

icepeng commented Nov 14, 2017

TypeScript Version: 2.6.1

Code

interface A {
  first: string;
  bList?: B[];
}

interface B {
  second: string;
  c?: C;
}

interface C {
  third: string;
}

const Q: A & { bList: (B & { c: C })[] } = {
  first: 'first',
  bList: [
    {
      second: 'second',
      c: {
        third: 'third',
      },
    },
  ],
};

Q.bList.map(item => {
  P(item);
});

function P(value: B & { c: C }) {
  console.log(value);
}

Expected behavior:
Until 2.5.1 this code did not generate a compilation error.
The type of bList is calculated as follows:

(undefined & (B & {
  c: C;
})[]) | (B[] & (B & {
  c: C;
})[])

And type of item

B & {
  c: C;
}

Actual behavior:
With 2.6.x, this code causes an error.
The type of bList is calculated as follows:

B[] & (B & {
  c: C[];
})[]

And type of item

B

The type of item is treated as B and an error occurs. Is this change specified in the changelog?

@ahejlsberg
Copy link
Member

This is an effect of #18438. We now remove empty intersection types (i.e. intersection types for which no values exist) from union types. In your example, this means that undefined & (B & { c: C })[] is removed when constructing the type for bList, leaving just B[] & (B & { c: C })[]. Then, as we look for a map member on that type, we end up picking the candidate from B[] and therefore item has type B. Previously we'd pick a candidate from (B & { c: C })[] because we didn't remove the empty intersection.

In general, when the constituent types of intersections have overlapping signatures, the intersection is order sensitive because overload resolution processes the signatures from left to right. Indeed, your example works if you change the type of Q to list A last:

const Q: { bList: (B & { c: C })[] } & A = { ... };  // Now works 

@ahejlsberg ahejlsberg added the Working as Intended The behavior described is the intended behavior; this is not a bug label Nov 15, 2017
@icepeng
Copy link
Author

icepeng commented Nov 16, 2017

It would be nice to write that the intersection type is order sensitive in the advanced types section of the typescript handbook.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@microsoft microsoft locked and limited conversation to collaborators Jun 14, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants