Skip to content

Commit 1edecac

Browse files
authored
Merge pull request #18126 from Microsoft/propertyRelations
Compare shapes of objects before comparing contained types
2 parents 541920e + 86930c9 commit 1edecac

8 files changed

+43
-92
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9436,21 +9436,20 @@ namespace ts {
94369436
if (relation === identityRelation) {
94379437
return propertiesIdenticalTo(source, target);
94389438
}
9439+
const requireOptionalProperties = relation === subtypeRelation && !(getObjectFlags(source) & ObjectFlags.ObjectLiteral);
9440+
const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties);
9441+
if (unmatchedProperty) {
9442+
if (reportErrors) {
9443+
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(unmatchedProperty), typeToString(source));
9444+
}
9445+
return Ternary.False;
9446+
}
94399447
let result = Ternary.True;
94409448
const properties = getPropertiesOfObjectType(target);
9441-
const requireOptionalProperties = relation === subtypeRelation && !(getObjectFlags(source) & ObjectFlags.ObjectLiteral);
94429449
for (const targetProp of properties) {
9443-
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
9444-
if (sourceProp !== targetProp) {
9445-
if (!sourceProp) {
9446-
if (!(targetProp.flags & SymbolFlags.Optional) || requireOptionalProperties) {
9447-
if (reportErrors) {
9448-
reportError(Diagnostics.Property_0_is_missing_in_type_1, symbolToString(targetProp), typeToString(source));
9449-
}
9450-
return Ternary.False;
9451-
}
9452-
}
9453-
else if (!(targetProp.flags & SymbolFlags.Prototype)) {
9450+
if (!(targetProp.flags & SymbolFlags.Prototype)) {
9451+
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
9452+
if (sourceProp && sourceProp !== targetProp) {
94549453
const sourcePropFlags = getDeclarationModifierFlagsFromSymbol(sourceProp);
94559454
const targetPropFlags = getDeclarationModifierFlagsFromSymbol(targetProp);
94569455
if (sourcePropFlags & ModifierFlags.Private || targetPropFlags & ModifierFlags.Private) {
@@ -10475,17 +10474,17 @@ namespace ts {
1047510474
}
1047610475
}
1047710476

10478-
function isPossiblyAssignableTo(source: Type, target: Type) {
10477+
function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) {
1047910478
const properties = getPropertiesOfObjectType(target);
1048010479
for (const targetProp of properties) {
10481-
if (!(targetProp.flags & (SymbolFlags.Optional | SymbolFlags.Prototype))) {
10482-
const sourceProp = getPropertyOfObjectType(source, targetProp.escapedName);
10480+
if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional)) {
10481+
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
1048310482
if (!sourceProp) {
10484-
return false;
10483+
return targetProp;
1048510484
}
1048610485
}
1048710486
}
10488-
return true;
10487+
return undefined;
1048910488
}
1049010489

1049110490
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0) {
@@ -10683,7 +10682,7 @@ namespace ts {
1068310682
}
1068410683
// Infer from the members of source and target only if the two types are possibly related. We check
1068510684
// in both directions because we may be inferring for a co-variant or a contra-variant position.
10686-
if (isPossiblyAssignableTo(source, target) || isPossiblyAssignableTo(target, source)) {
10685+
if (!getUnmatchedProperty(source, target, /*requireOptionalProperties*/ false) || !getUnmatchedProperty(target, source, /*requireOptionalProperties*/ false)) {
1068710686
inferFromProperties(source, target);
1068810687
inferFromSignatures(source, target, SignatureKind.Call);
1068910688
inferFromSignatures(source, target, SignatureKind.Construct);

tests/baselines/reference/arityAndOrderCompatibility01.errors.txt

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,11 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(14,12): erro
33
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(15,5): error TS2461: Type '{ 0: string; 1: number; }' is not an array type.
44
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(15,12): error TS2460: Type '{ 0: string; 1: number; }' has no property '2'.
55
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(16,5): error TS2322: Type '[string, number]' is not assignable to type '[number, number, number]'.
6-
Types of property '0' are incompatible.
7-
Type 'string' is not assignable to type 'number'.
6+
Property '2' is missing in type '[string, number]'.
87
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(17,5): error TS2322: Type 'StrNum' is not assignable to type '[number, number, number]'.
9-
Types of property '0' are incompatible.
10-
Type 'string' is not assignable to type 'number'.
8+
Property '2' is missing in type 'StrNum'.
119
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(18,5): error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[number, number, number]'.
12-
Types of property '0' are incompatible.
13-
Type 'string' is not assignable to type 'number'.
10+
Property '2' is missing in type '{ 0: string; 1: number; }'.
1411
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(19,5): error TS2322: Type '[string, number]' is not assignable to type '[string, number, number]'.
1512
Property '2' is missing in type '[string, number]'.
1613
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(20,5): error TS2322: Type 'StrNum' is not assignable to type '[string, number, number]'.
@@ -24,8 +21,7 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(23,5): error
2421
Types of property '0' are incompatible.
2522
Type 'string' is not assignable to type 'number'.
2623
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(24,5): error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[number]'.
27-
Types of property '0' are incompatible.
28-
Type 'string' is not assignable to type 'number'.
24+
Property 'length' is missing in type '{ 0: string; 1: number; }'.
2925
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(25,5): error TS2322: Type '[string, number]' is not assignable to type '[string]'.
3026
Types of property 'pop' are incompatible.
3127
Type '() => string | number' is not assignable to type '() => string'.
@@ -44,8 +40,7 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(29,5): error
4440
Types of property '0' are incompatible.
4541
Type 'string' is not assignable to type 'number'.
4642
tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(30,5): error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[number, string]'.
47-
Types of property '0' are incompatible.
48-
Type 'string' is not assignable to type 'number'.
43+
Property 'length' is missing in type '{ 0: string; 1: number; }'.
4944

5045

5146
==== tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts (19 errors) ====
@@ -75,18 +70,15 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(30,5): error
7570
var j1: [number, number, number] = x;
7671
~~
7772
!!! error TS2322: Type '[string, number]' is not assignable to type '[number, number, number]'.
78-
!!! error TS2322: Types of property '0' are incompatible.
79-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
73+
!!! error TS2322: Property '2' is missing in type '[string, number]'.
8074
var j2: [number, number, number] = y;
8175
~~
8276
!!! error TS2322: Type 'StrNum' is not assignable to type '[number, number, number]'.
83-
!!! error TS2322: Types of property '0' are incompatible.
84-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
77+
!!! error TS2322: Property '2' is missing in type 'StrNum'.
8578
var j3: [number, number, number] = z;
8679
~~
8780
!!! error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[number, number, number]'.
88-
!!! error TS2322: Types of property '0' are incompatible.
89-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
81+
!!! error TS2322: Property '2' is missing in type '{ 0: string; 1: number; }'.
9082
var k1: [string, number, number] = x;
9183
~~
9284
!!! error TS2322: Type '[string, number]' is not assignable to type '[string, number, number]'.
@@ -112,8 +104,7 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(30,5): error
112104
var l3: [number] = z;
113105
~~
114106
!!! error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[number]'.
115-
!!! error TS2322: Types of property '0' are incompatible.
116-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
107+
!!! error TS2322: Property 'length' is missing in type '{ 0: string; 1: number; }'.
117108
var m1: [string] = x;
118109
~~
119110
!!! error TS2322: Type '[string, number]' is not assignable to type '[string]'.
@@ -144,8 +135,7 @@ tests/cases/conformance/types/tuple/arityAndOrderCompatibility01.ts(30,5): error
144135
var n3: [number, string] = z;
145136
~~
146137
!!! error TS2322: Type '{ 0: string; 1: number; }' is not assignable to type '[number, string]'.
147-
!!! error TS2322: Types of property '0' are incompatible.
148-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
138+
!!! error TS2322: Property 'length' is missing in type '{ 0: string; 1: number; }'.
149139
var o1: [string, number] = x;
150140
var o2: [string, number] = y;
151141
var o3: [string, number] = y;

tests/baselines/reference/contextualTypeWithUnionTypeObjectLiteral.errors.txt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,7 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
1515
Type 'string' is not assignable to type 'number'.
1616
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(25,5): error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'.
1717
Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: number; anotherP1: number; }'.
18-
Types of property 'prop' are incompatible.
19-
Type 'string | number' is not assignable to type 'number'.
20-
Type 'string' is not assignable to type 'number'.
18+
Property 'anotherP1' is missing in type '{ prop: string | number; anotherP: string; }'.
2119
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(29,5): error TS2322: Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'.
2220
Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: number; anotherP1: number; }'.
2321
Types of property 'prop' are incompatible.
@@ -78,9 +76,7 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
7876
~~~~~~~~~~~~
7977
!!! error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'.
8078
!!! error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: number; anotherP1: number; }'.
81-
!!! error TS2322: Types of property 'prop' are incompatible.
82-
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
83-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
79+
!!! error TS2322: Property 'anotherP1' is missing in type '{ prop: string | number; anotherP: string; }'.
8480
prop: strOrNumber,
8581
anotherP: str
8682
};

tests/baselines/reference/promisePermutations.errors.txt

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,7 @@ tests/cases/compiler/promisePermutations.ts(144,35): error TS2345: Argument of t
4848
Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
4949
tests/cases/compiler/promisePermutations.ts(152,36): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => Promise<number>'.
5050
Type 'IPromise<string>' is not assignable to type 'Promise<number>'.
51-
Types of property 'then' are incompatible.
52-
Type '{ <U>(success?: (value: string) => IPromise<U>, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => IPromise<U>, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => U, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => U, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>; }' is not assignable to type '{ <TResult1 = number, TResult2 = never>(onfulfilled?: (value: number) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>; <U>(success?: (value: number) => Promise<U>, error?: (error: any) => Promise<U>, progress?: (progress: any) => void): Promise<U>; <U>(success?: (value: number) => Promise<U>, error?: (error: any) => U, progress?: (progress: any) => void): Promise<U>; <U>(success?: (value: number) => U, error?: (error: any) => Promise<U>, progress?: (progress: any) => void): Promise<U>; <U>(success?: (value: number) => U, error?: (error: any) => U, progress?: (progress: any) => void): Promise<U>; }'.
53-
Types of parameters 'success' and 'onfulfilled' are incompatible.
54-
Types of parameters 'value' and 'value' are incompatible.
55-
Type 'string' is not assignable to type 'number'.
51+
Property 'catch' is missing in type 'IPromise<string>'.
5652
tests/cases/compiler/promisePermutations.ts(156,21): error TS2345: Argument of type '{ (x: number): IPromise<number>; (x: string): IPromise<string>; }' is not assignable to parameter of type '(value: number) => IPromise<string>'.
5753
Type 'IPromise<number>' is not assignable to type 'IPromise<string>'.
5854
Type 'number' is not assignable to type 'string'.
@@ -302,11 +298,7 @@ tests/cases/compiler/promisePermutations.ts(160,21): error TS2345: Argument of t
302298
~~~~~~~~~
303299
!!! error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => Promise<number>'.
304300
!!! error TS2345: Type 'IPromise<string>' is not assignable to type 'Promise<number>'.
305-
!!! error TS2345: Types of property 'then' are incompatible.
306-
!!! error TS2345: Type '{ <U>(success?: (value: string) => IPromise<U>, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => IPromise<U>, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => U, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => U, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>; }' is not assignable to type '{ <TResult1 = number, TResult2 = never>(onfulfilled?: (value: number) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>; <U>(success?: (value: number) => Promise<U>, error?: (error: any) => Promise<U>, progress?: (progress: any) => void): Promise<U>; <U>(success?: (value: number) => Promise<U>, error?: (error: any) => U, progress?: (progress: any) => void): Promise<U>; <U>(success?: (value: number) => U, error?: (error: any) => Promise<U>, progress?: (progress: any) => void): Promise<U>; <U>(success?: (value: number) => U, error?: (error: any) => U, progress?: (progress: any) => void): Promise<U>; }'.
307-
!!! error TS2345: Types of parameters 'success' and 'onfulfilled' are incompatible.
308-
!!! error TS2345: Types of parameters 'value' and 'value' are incompatible.
309-
!!! error TS2345: Type 'string' is not assignable to type 'number'.
301+
!!! error TS2345: Property 'catch' is missing in type 'IPromise<string>'.
310302
var s10g = s10.then(testFunctionP, nIPromise, sIPromise).then(sPromise, sIPromise, sIPromise); // ok
311303

312304
var r11: IPromise<number>;

tests/baselines/reference/promisePermutations2.errors.txt

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,7 @@ tests/cases/compiler/promisePermutations2.ts(143,35): error TS2345: Argument of
4848
Type 'IPromise<string>' is not assignable to type 'IPromise<number>'.
4949
tests/cases/compiler/promisePermutations2.ts(151,36): error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => Promise<number>'.
5050
Type 'IPromise<string>' is not assignable to type 'Promise<number>'.
51-
Types of property 'then' are incompatible.
52-
Type '{ <U>(success?: (value: string) => IPromise<U>, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => IPromise<U>, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => U, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => U, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>; }' is not assignable to type '{ <TResult1 = number, TResult2 = never>(onfulfilled?: (value: number) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>; <U>(success?: (value: number) => U, error?: (error: any) => U, progress?: (progress: any) => void): Promise<U>; }'.
53-
Types of parameters 'success' and 'onfulfilled' are incompatible.
54-
Types of parameters 'value' and 'value' are incompatible.
55-
Type 'string' is not assignable to type 'number'.
51+
Property 'catch' is missing in type 'IPromise<string>'.
5652
tests/cases/compiler/promisePermutations2.ts(155,21): error TS2345: Argument of type '{ (x: number): IPromise<number>; (x: string): IPromise<string>; }' is not assignable to parameter of type '(value: number) => IPromise<string>'.
5753
Type 'IPromise<number>' is not assignable to type 'IPromise<string>'.
5854
Type 'number' is not assignable to type 'string'.
@@ -301,11 +297,7 @@ tests/cases/compiler/promisePermutations2.ts(159,21): error TS2345: Argument of
301297
~~~~~~~~~
302298
!!! error TS2345: Argument of type '(x: any) => IPromise<string>' is not assignable to parameter of type '(error: any) => Promise<number>'.
303299
!!! error TS2345: Type 'IPromise<string>' is not assignable to type 'Promise<number>'.
304-
!!! error TS2345: Types of property 'then' are incompatible.
305-
!!! error TS2345: Type '{ <U>(success?: (value: string) => IPromise<U>, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => IPromise<U>, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => U, error?: (error: any) => IPromise<U>, progress?: (progress: any) => void): IPromise<U>; <U>(success?: (value: string) => U, error?: (error: any) => U, progress?: (progress: any) => void): IPromise<U>; }' is not assignable to type '{ <TResult1 = number, TResult2 = never>(onfulfilled?: (value: number) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>): Promise<TResult1 | TResult2>; <U>(success?: (value: number) => U, error?: (error: any) => U, progress?: (progress: any) => void): Promise<U>; }'.
306-
!!! error TS2345: Types of parameters 'success' and 'onfulfilled' are incompatible.
307-
!!! error TS2345: Types of parameters 'value' and 'value' are incompatible.
308-
!!! error TS2345: Type 'string' is not assignable to type 'number'.
300+
!!! error TS2345: Property 'catch' is missing in type 'IPromise<string>'.
309301
var s10g = s10.then(testFunctionP, nIPromise, sIPromise).then(sPromise, sIPromise, sIPromise); // ok
310302

311303
var r11: IPromise<number>;

0 commit comments

Comments
 (0)