Skip to content

Commit

Permalink
Cherry-pick PR #34789 into release-3.7 (#34812)
Browse files Browse the repository at this point in the history
Component commits:
2e0b451 Add isIntersectionConstituent to relation key
isIntersectionConstituent controls whether relation checking performs
excess property and common property checks. It is possible to fail a
relation check with excess property checks turned on, cache the result,
and then skip a relation check with excess property checks that would
have succeeded. #33133 provides an example of such a program.

Fixes #33133 the right way, so I reverted the fix at #33213
Fixes #34762 (by reverting #33213)
Fixes #33944 -- I added the test from #34646

14d7a44 Merge branch 'master' into add-isIntersectionConstituent-to-relation-key

ea80362 Update comments in test

0764275 Merge branch 'master' into add-isIntersectionConstituent-to-relation-key
  • Loading branch information
typescript-bot authored and sandersn committed Oct 29, 2019
1 parent 7d77ecb commit 28050d5
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 45 deletions.
19 changes: 10 additions & 9 deletions src/compiler/checker.ts
Expand Up @@ -14160,7 +14160,7 @@ namespace ts {
return true;
}
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
const related = relation.get(getRelationKey(source, target, relation));
const related = relation.get(getRelationKey(source, target, /*isIntersectionConstituent*/ false, relation));
if (related !== undefined) {
return !!(related & RelationComparisonResult.Succeeded);
}
Expand Down Expand Up @@ -14566,7 +14566,7 @@ namespace ts {
// and we need to handle "each" relations before "some" relations for the same kind of type.
if (source.flags & TypeFlags.Union) {
result = relation === comparableRelation ?
someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), isIntersectionConstituent) :
someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)) :
eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive));
}
else {
Expand Down Expand Up @@ -14604,7 +14604,7 @@ namespace ts {
//
// - For a primitive type or type parameter (such as 'number = A & B') there is no point in
// breaking the intersection apart.
result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ true);
result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false);
}
if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) {
if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent)) {
Expand Down Expand Up @@ -14898,14 +14898,14 @@ namespace ts {
return result;
}

function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean): Ternary {
function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
const sourceTypes = source.types;
if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) {
return Ternary.True;
}
const len = sourceTypes.length;
for (let i = 0; i < len; i++) {
const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1, /*headMessage*/ undefined, isIntersectionConstituent);
const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1);
if (related) {
return related;
}
Expand Down Expand Up @@ -14992,7 +14992,7 @@ namespace ts {
if (overflow) {
return Ternary.False;
}
const id = getRelationKey(source, target, relation);
const id = getRelationKey(source, target, isIntersectionConstituent, relation);
const entry = relation.get(id);
if (entry !== undefined) {
if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
Expand Down Expand Up @@ -16206,17 +16206,18 @@ namespace ts {
* To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters.
* For other cases, the types ids are used.
*/
function getRelationKey(source: Type, target: Type, relation: Map<RelationComparisonResult>) {
function getRelationKey(source: Type, target: Type, isIntersectionConstituent: boolean, relation: Map<RelationComparisonResult>) {
if (relation === identityRelation && source.id > target.id) {
const temp = source;
source = target;
target = temp;
}
const intersection = isIntersectionConstituent ? "&" : "";
if (isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target)) {
const typeParameters: Type[] = [];
return getTypeReferenceId(<TypeReference>source, typeParameters) + "," + getTypeReferenceId(<TypeReference>target, typeParameters);
return getTypeReferenceId(<TypeReference>source, typeParameters) + "," + getTypeReferenceId(<TypeReference>target, typeParameters) + intersection;
}
return source.id + "," + target.id;
return source.id + "," + target.id + intersection;
}

// Invoke the callback for each underlying property symbol of the given symbol and return the first
Expand Down
26 changes: 26 additions & 0 deletions tests/baselines/reference/commonTypeIntersection.errors.txt
@@ -0,0 +1,26 @@
tests/cases/conformance/types/intersection/commonTypeIntersection.ts(2,5): error TS2322: Type '{ __typename?: "TypeTwo"; } & { a: boolean; }' is not assignable to type '{ __typename?: "TypeOne"; } & { a: boolean; }'.
Type '{ __typename?: "TypeTwo"; } & { a: boolean; }' is not assignable to type '{ __typename?: "TypeOne"; }'.
Types of property '__typename' are incompatible.
Type '"TypeTwo"' is not assignable to type '"TypeOne"'.
tests/cases/conformance/types/intersection/commonTypeIntersection.ts(4,5): error TS2322: Type '{ __typename?: "TypeTwo"; } & string' is not assignable to type '{ __typename?: "TypeOne"; } & string'.
Type '{ __typename?: "TypeTwo"; } & string' is not assignable to type '{ __typename?: "TypeOne"; }'.
Types of property '__typename' are incompatible.
Type '"TypeTwo"' is not assignable to type '"TypeOne"'.


==== tests/cases/conformance/types/intersection/commonTypeIntersection.ts (2 errors) ====
declare let x1: { __typename?: 'TypeTwo' } & { a: boolean };
let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // should error here
~~
!!! error TS2322: Type '{ __typename?: "TypeTwo"; } & { a: boolean; }' is not assignable to type '{ __typename?: "TypeOne"; } & { a: boolean; }'.
!!! error TS2322: Type '{ __typename?: "TypeTwo"; } & { a: boolean; }' is not assignable to type '{ __typename?: "TypeOne"; }'.
!!! error TS2322: Types of property '__typename' are incompatible.
!!! error TS2322: Type '"TypeTwo"' is not assignable to type '"TypeOne"'.
declare let x2: { __typename?: 'TypeTwo' } & string;
let y2: { __typename?: 'TypeOne' } & string = x2; // should error here
~~
!!! error TS2322: Type '{ __typename?: "TypeTwo"; } & string' is not assignable to type '{ __typename?: "TypeOne"; } & string'.
!!! error TS2322: Type '{ __typename?: "TypeTwo"; } & string' is not assignable to type '{ __typename?: "TypeOne"; }'.
!!! error TS2322: Types of property '__typename' are incompatible.
!!! error TS2322: Type '"TypeTwo"' is not assignable to type '"TypeOne"'.

10 changes: 10 additions & 0 deletions tests/baselines/reference/commonTypeIntersection.js
@@ -0,0 +1,10 @@
//// [commonTypeIntersection.ts]
declare let x1: { __typename?: 'TypeTwo' } & { a: boolean };
let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // should error here
declare let x2: { __typename?: 'TypeTwo' } & string;
let y2: { __typename?: 'TypeOne' } & string = x2; // should error here


//// [commonTypeIntersection.js]
var y1 = x1; // should error here
var y2 = x2; // should error here
21 changes: 21 additions & 0 deletions tests/baselines/reference/commonTypeIntersection.symbols
@@ -0,0 +1,21 @@
=== tests/cases/conformance/types/intersection/commonTypeIntersection.ts ===
declare let x1: { __typename?: 'TypeTwo' } & { a: boolean };
>x1 : Symbol(x1, Decl(commonTypeIntersection.ts, 0, 11))
>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 0, 17))
>a : Symbol(a, Decl(commonTypeIntersection.ts, 0, 46))

let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // should error here
>y1 : Symbol(y1, Decl(commonTypeIntersection.ts, 1, 3))
>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 1, 9))
>a : Symbol(a, Decl(commonTypeIntersection.ts, 1, 38))
>x1 : Symbol(x1, Decl(commonTypeIntersection.ts, 0, 11))

declare let x2: { __typename?: 'TypeTwo' } & string;
>x2 : Symbol(x2, Decl(commonTypeIntersection.ts, 2, 11))
>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 2, 17))

let y2: { __typename?: 'TypeOne' } & string = x2; // should error here
>y2 : Symbol(y2, Decl(commonTypeIntersection.ts, 3, 3))
>__typename : Symbol(__typename, Decl(commonTypeIntersection.ts, 3, 9))
>x2 : Symbol(x2, Decl(commonTypeIntersection.ts, 2, 11))

21 changes: 21 additions & 0 deletions tests/baselines/reference/commonTypeIntersection.types
@@ -0,0 +1,21 @@
=== tests/cases/conformance/types/intersection/commonTypeIntersection.ts ===
declare let x1: { __typename?: 'TypeTwo' } & { a: boolean };
>x1 : { __typename?: "TypeTwo"; } & { a: boolean; }
>__typename : "TypeTwo"
>a : boolean

let y1: { __typename?: 'TypeOne' } & { a: boolean} = x1; // should error here
>y1 : { __typename?: "TypeOne"; } & { a: boolean; }
>__typename : "TypeOne"
>a : boolean
>x1 : { __typename?: "TypeTwo"; } & { a: boolean; }

declare let x2: { __typename?: 'TypeTwo' } & string;
>x2 : { __typename?: "TypeTwo"; } & string
>__typename : "TypeTwo"

let y2: { __typename?: 'TypeOne' } & string = x2; // should error here
>y2 : { __typename?: "TypeOne"; } & string
>__typename : "TypeOne"
>x2 : { __typename?: "TypeTwo"; } & string

Expand Up @@ -5,20 +5,23 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
Type '"text"' is not assignable to type 'T & "text"'.
Type '"text"' is not assignable to type 'T'.
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
Type 'T' is not assignable to type 'T & "text"'.
Type '"text" | "email"' is not assignable to type 'T & "text"'.
Type '"text"' is not assignable to type 'T & "text"'.
Type '"text"' is not assignable to type 'T'.
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
Type 'T' is not assignable to type '"text"'.
Type '"text" | "email"' is not assignable to type '"text"'.
Type '"email"' is not assignable to type '"text"'.
Type '"text"' is not assignable to type 'T & "text"'.
Type '"text"' is not assignable to type 'T'.
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
Type '"text"' is not assignable to type 'T & "text"'.
Type '"text"' is not assignable to type 'T'.
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
Type 'T' is not assignable to type 'T & "text"'.
Type '"text" | "email"' is not assignable to type 'T & "text"'.
Type '"text"' is not assignable to type 'T & "text"'.
Type '"text"' is not assignable to type 'T'.
'"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
Type 'T' is not assignable to type '"text"'.
Type '"text" | "email"' is not assignable to type '"text"'.
Type '"email"' is not assignable to type '"text"'.


==== tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.ts (1 errors) ====
Expand Down Expand Up @@ -63,20 +66,23 @@ tests/cases/compiler/complicatedIndexedAccessKeyofReliesOnKeyofNeverUpperBound.t
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"] & ChannelOfType<T, EmailChannel>["type"]'.
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
!!! error TS2322: Type 'T' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
!!! error TS2322: Type '"text"' is not assignable to type 'ChannelOfType<T, TextChannel>["type"]'.
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
!!! error TS2322: Type 'T' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text" | "email"' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text"' is not assignable to type 'T & "text"'.
!!! error TS2322: Type '"text"' is not assignable to type 'T'.
!!! error TS2322: '"text"' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '"text" | "email"'.
!!! error TS2322: Type 'T' is not assignable to type '"text"'.
!!! error TS2322: Type '"text" | "email"' is not assignable to type '"text"'.
!!! error TS2322: Type '"email"' is not assignable to type '"text"'.
}

const newTextChannel = makeNewChannel('text');
Expand Down

0 comments on commit 28050d5

Please sign in to comment.