Skip to content

Commit d293b67

Browse files
authored
Merge pull request microsoft#26790 from Microsoft/fixWeakObjectRelationCheck
Fix weak object relation check
2 parents b687caf + f597e2e commit d293b67

8 files changed

+87
-103
lines changed

src/compiler/checker.ts

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -11190,13 +11190,10 @@ namespace ts {
1119011190
}
1119111191
}
1119211192

11193-
if (relation !== comparableRelation &&
11194-
!(source.flags & TypeFlags.Union) &&
11195-
!(target.flags & TypeFlags.Union) &&
11196-
!isIntersectionConstituent &&
11197-
source !== globalObjectType &&
11193+
if (relation !== comparableRelation && !isIntersectionConstituent &&
11194+
source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType &&
11195+
target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) &&
1119811196
(getPropertiesOfType(source).length > 0 || typeHasCallOrConstructSignatures(source)) &&
11199-
isWeakType(target) &&
1120011197
!hasCommonProperties(source, target)) {
1120111198
if (reportErrors) {
1120211199
const calls = getSignaturesOfType(source, SignatureKind.Call);
@@ -11841,6 +11838,9 @@ namespace ts {
1184111838
errorInfo = saveErrorInfo;
1184211839
}
1184311840
}
11841+
else if (isTupleType(source) && (isArrayType(target) || isReadonlyArrayType(target)) || isArrayType(source) && isReadonlyArrayType(target)) {
11842+
return isRelatedTo(getIndexTypeOfType(source, IndexKind.Number) || anyType, getIndexTypeOfType(target, IndexKind.Number) || anyType, reportErrors);
11843+
}
1184411844
// Even if relationship doesn't hold for unions, intersections, or generic type references,
1184511845
// it may hold in a structural comparison.
1184611846
// In a check of the form X = A & B, we will have previously checked if A relates to X or B relates
@@ -12020,34 +12020,6 @@ namespace ts {
1202012020
return result;
1202112021
}
1202212022

12023-
/**
12024-
* A type is 'weak' if it is an object type with at least one optional property
12025-
* and no required properties, call/construct signatures or index signatures
12026-
*/
12027-
function isWeakType(type: Type): boolean {
12028-
if (type.flags & TypeFlags.Object) {
12029-
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
12030-
return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 &&
12031-
!resolved.stringIndexInfo && !resolved.numberIndexInfo &&
12032-
resolved.properties.length > 0 &&
12033-
every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional));
12034-
}
12035-
if (type.flags & TypeFlags.Intersection) {
12036-
return every((<IntersectionType>type).types, isWeakType);
12037-
}
12038-
return false;
12039-
}
12040-
12041-
function hasCommonProperties(source: Type, target: Type) {
12042-
const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
12043-
for (const prop of getPropertiesOfType(source)) {
12044-
if (isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
12045-
return true;
12046-
}
12047-
}
12048-
return false;
12049-
}
12050-
1205112023
function propertiesIdenticalTo(source: Type, target: Type): Ternary {
1205212024
if (!(source.flags & TypeFlags.Object && target.flags & TypeFlags.Object)) {
1205312025
return Ternary.False;
@@ -12292,6 +12264,34 @@ namespace ts {
1229212264
}
1229312265
}
1229412266

12267+
/**
12268+
* A type is 'weak' if it is an object type with at least one optional property
12269+
* and no required properties, call/construct signatures or index signatures
12270+
*/
12271+
function isWeakType(type: Type): boolean {
12272+
if (type.flags & TypeFlags.Object) {
12273+
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
12274+
return resolved.callSignatures.length === 0 && resolved.constructSignatures.length === 0 &&
12275+
!resolved.stringIndexInfo && !resolved.numberIndexInfo &&
12276+
resolved.properties.length > 0 &&
12277+
every(resolved.properties, p => !!(p.flags & SymbolFlags.Optional));
12278+
}
12279+
if (type.flags & TypeFlags.Intersection) {
12280+
return every((<IntersectionType>type).types, isWeakType);
12281+
}
12282+
return false;
12283+
}
12284+
12285+
function hasCommonProperties(source: Type, target: Type) {
12286+
const isComparingJsxAttributes = !!(getObjectFlags(source) & ObjectFlags.JsxAttributes);
12287+
for (const prop of getPropertiesOfType(source)) {
12288+
if (isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
12289+
return true;
12290+
}
12291+
}
12292+
return false;
12293+
}
12294+
1229512295
// Return a type reference where the source type parameter is replaced with the target marker
1229612296
// type, and flag the result as a marker type reference.
1229712297
function getMarkerTypeReference(type: GenericType, source: TypeParameter, target: Type) {

tests/baselines/reference/arrayOfSubtypeIsAssignableToReadonlyArray.errors.txt

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(13,1): error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray<B>'.
2-
Types of property 'concat' are incompatible.
3-
Type '{ (...items: ConcatArray<A>[]): A[]; (...items: (A | ConcatArray<A>)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray<B>[]): B[]; (...items: (B | ConcatArray<B>)[]): B[]; }'.
4-
Type 'A[]' is not assignable to type 'B[]'.
5-
Type 'A' is not assignable to type 'B'.
6-
Property 'b' is missing in type 'A'.
2+
Type 'A' is not assignable to type 'B'.
3+
Property 'b' is missing in type 'A'.
74
tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error TS2322: Type 'C<A>' is not assignable to type 'ReadonlyArray<B>'.
85
Types of property 'concat' are incompatible.
96
Type '{ (...items: ConcatArray<A>[]): A[]; (...items: (A | ConcatArray<A>)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray<B>[]): B[]; (...items: (B | ConcatArray<B>)[]): B[]; }'.
107
Type 'A[]' is not assignable to type 'B[]'.
8+
Type 'A' is not assignable to type 'B'.
119

1210

1311
==== tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts (2 errors) ====
@@ -26,11 +24,8 @@ tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error T
2624
rrb = ara; // error: 'A' is not assignable to 'B'
2725
~~~
2826
!!! error TS2322: Type 'A[]' is not assignable to type 'ReadonlyArray<B>'.
29-
!!! error TS2322: Types of property 'concat' are incompatible.
30-
!!! error TS2322: Type '{ (...items: ConcatArray<A>[]): A[]; (...items: (A | ConcatArray<A>)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray<B>[]): B[]; (...items: (B | ConcatArray<B>)[]): B[]; }'.
31-
!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'.
32-
!!! error TS2322: Type 'A' is not assignable to type 'B'.
33-
!!! error TS2322: Property 'b' is missing in type 'A'.
27+
!!! error TS2322: Type 'A' is not assignable to type 'B'.
28+
!!! error TS2322: Property 'b' is missing in type 'A'.
3429

3530
rra = cra;
3631
rra = crb; // OK, C<B> is assignable to ReadonlyArray<A>
@@ -41,4 +36,5 @@ tests/cases/compiler/arrayOfSubtypeIsAssignableToReadonlyArray.ts(18,1): error T
4136
!!! error TS2322: Types of property 'concat' are incompatible.
4237
!!! error TS2322: Type '{ (...items: ConcatArray<A>[]): A[]; (...items: (A | ConcatArray<A>)[]): A[]; }' is not assignable to type '{ (...items: ConcatArray<B>[]): B[]; (...items: (B | ConcatArray<B>)[]): B[]; }'.
4338
!!! error TS2322: Type 'A[]' is not assignable to type 'B[]'.
39+
!!! error TS2322: Type 'A' is not assignable to type 'B'.
4440

tests/baselines/reference/assignmentCompatBetweenTupleAndArray.errors.txt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatBetweenTupleAndArray.ts(17,1): error TS2322: Type '[number, string]' is not assignable to type 'number[]'.
2-
Types of property 'pop' are incompatible.
3-
Type '() => string | number' is not assignable to type '() => number'.
4-
Type 'string | number' is not assignable to type 'number'.
5-
Type 'string' is not assignable to type 'number'.
2+
Type 'string | number' is not assignable to type 'number'.
3+
Type 'string' is not assignable to type 'number'.
64
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatBetweenTupleAndArray.ts(18,1): error TS2322: Type '{}[]' is not assignable to type '[{}]'.
75
Property '0' is missing in type '{}[]'.
86

@@ -27,10 +25,8 @@ tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignme
2725
numArray = numStrTuple;
2826
~~~~~~~~
2927
!!! error TS2322: Type '[number, string]' is not assignable to type 'number[]'.
30-
!!! error TS2322: Types of property 'pop' are incompatible.
31-
!!! error TS2322: Type '() => string | number' is not assignable to type '() => number'.
32-
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
33-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
28+
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
29+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
3430
emptyObjTuple = emptyObjArray;
3531
~~~~~~~~~~~~~
3632
!!! error TS2322: Type '{}[]' is not assignable to type '[{}]'.

tests/baselines/reference/for-of39.errors.txt

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,16 @@
11
tests/cases/conformance/es6/for-ofStatements/for-of39.ts(1,19): error TS2345: Argument of type '([string, number] | [string, true])[]' is not assignable to parameter of type 'ReadonlyArray<[string, boolean]>'.
2-
Types of property 'concat' are incompatible.
3-
Type '{ (...items: ConcatArray<[string, number] | [string, true]>[]): ([string, number] | [string, true])[]; (...items: ([string, number] | [string, true] | ConcatArray<[string, number] | [string, true]>)[]): ([string, number] | [string, true])[]; }' is not assignable to type '{ (...items: ConcatArray<[string, boolean]>[]): [string, boolean][]; (...items: ([string, boolean] | ConcatArray<[string, boolean]>)[]): [string, boolean][]; }'.
4-
Types of parameters 'items' and 'items' are incompatible.
5-
Type 'ConcatArray<[string, boolean]>' is not assignable to type 'ConcatArray<[string, number] | [string, true]>'.
6-
Type '[string, boolean]' is not assignable to type '[string, number] | [string, true]'.
7-
Type '[string, boolean]' is not assignable to type '[string, number]'.
8-
Type 'boolean' is not assignable to type 'number'.
2+
Type '[string, number] | [string, true]' is not assignable to type '[string, boolean]'.
3+
Type '[string, number]' is not assignable to type '[string, boolean]'.
4+
Type 'number' is not assignable to type 'boolean'.
95

106

117
==== tests/cases/conformance/es6/for-ofStatements/for-of39.ts (1 errors) ====
128
var map = new Map([["", true], ["", 0]]);
139
~~~~~~~~~~~~~~~~~~~~~
1410
!!! error TS2345: Argument of type '([string, number] | [string, true])[]' is not assignable to parameter of type 'ReadonlyArray<[string, boolean]>'.
15-
!!! error TS2345: Types of property 'concat' are incompatible.
16-
!!! error TS2345: Type '{ (...items: ConcatArray<[string, number] | [string, true]>[]): ([string, number] | [string, true])[]; (...items: ([string, number] | [string, true] | ConcatArray<[string, number] | [string, true]>)[]): ([string, number] | [string, true])[]; }' is not assignable to type '{ (...items: ConcatArray<[string, boolean]>[]): [string, boolean][]; (...items: ([string, boolean] | ConcatArray<[string, boolean]>)[]): [string, boolean][]; }'.
17-
!!! error TS2345: Types of parameters 'items' and 'items' are incompatible.
18-
!!! error TS2345: Type 'ConcatArray<[string, boolean]>' is not assignable to type 'ConcatArray<[string, number] | [string, true]>'.
19-
!!! error TS2345: Type '[string, boolean]' is not assignable to type '[string, number] | [string, true]'.
20-
!!! error TS2345: Type '[string, boolean]' is not assignable to type '[string, number]'.
21-
!!! error TS2345: Type 'boolean' is not assignable to type 'number'.
11+
!!! error TS2345: Type '[string, number] | [string, true]' is not assignable to type '[string, boolean]'.
12+
!!! error TS2345: Type '[string, number]' is not assignable to type '[string, boolean]'.
13+
!!! error TS2345: Type 'number' is not assignable to type 'boolean'.
2214
for (var [k, v] of map) {
2315
k;
2416
v;

tests/baselines/reference/infiniteConstraints.errors.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, string | number | symbol>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, string | number | symbol>>], Record<"val", string>>["val"]'.
12
tests/cases/compiler/infiniteConstraints.ts(4,37): error TS2536: Type '"val"' cannot be used to index type 'B[Exclude<keyof B, K>]'.
3+
tests/cases/compiler/infiniteConstraints.ts(27,37): error TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
4+
tests/cases/compiler/infiniteConstraints.ts(27,58): error TS2322: Type 'Record<"val", "test2">' is not assignable to type 'never'.
5+
tests/cases/compiler/infiniteConstraints.ts(29,45): error TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
26
tests/cases/compiler/infiniteConstraints.ts(31,43): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
37
tests/cases/compiler/infiniteConstraints.ts(31,63): error TS2322: Type 'Record<"val", "dup">' is not assignable to type 'never'.
48
tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' cannot be used to index type 'T[keyof T]'.
59

610

7-
==== tests/cases/compiler/infiniteConstraints.ts (4 errors) ====
11+
!!! error TS2321: Excessive stack depth comparing types 'Extract<T[Exclude<keyof T, string | number | symbol>], Record<"val", string>>["val"]' and 'Extract<T[Exclude<keyof T, Exclude<keyof T, string | number | symbol>>], Record<"val", string>>["val"]'.
12+
==== tests/cases/compiler/infiniteConstraints.ts (7 errors) ====
813
// Both of the following types trigger the recursion limiter in getImmediateBaseConstraint
914

1015
type T1<B extends { [K in keyof B]: Extract<B[Exclude<keyof B, K>], { val: string }>["val"] }> = B;
@@ -34,8 +39,17 @@ tests/cases/compiler/infiniteConstraints.ts(36,71): error TS2536: Type '"foo"' c
3439
>(vals: T): void;
3540

3641
const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});
42+
~~~~
43+
!!! error TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
44+
!!! related TS6500 tests/cases/compiler/infiniteConstraints.ts:27:37: The expected type comes from property 'main' which is declared here on type '{ main: never; alternate: never; }'
45+
~~~~~~~~~
46+
!!! error TS2322: Type 'Record<"val", "test2">' is not assignable to type 'never'.
47+
!!! related TS6500 tests/cases/compiler/infiniteConstraints.ts:27:58: The expected type comes from property 'alternate' which is declared here on type '{ main: never; alternate: never; }'
3748

3849
const shouldBeNoError = ensureNoDuplicates({main: value("test")});
50+
~~~~
51+
!!! error TS2322: Type 'Record<"val", "test">' is not assignable to type 'never'.
52+
!!! related TS6500 tests/cases/compiler/infiniteConstraints.ts:29:45: The expected type comes from property 'main' which is declared here on type '{ main: never; }'
3953

4054
const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
4155
~~~~

tests/baselines/reference/infiniteConstraints.types

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ declare function value<V extends string>(val: V): Value<V>;
3939
>val : V
4040

4141
declare function ensureNoDuplicates<
42-
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
42+
>ensureNoDuplicates : <T extends { [K in keyof T]: never; }>(vals: T) => void
4343

4444
T extends {
4545
[K in keyof T]: Extract<T[K], Value>["val"] extends Extract<T[Exclude<keyof T, K>], Value>["val"]
@@ -50,9 +50,9 @@ declare function ensureNoDuplicates<
5050
>vals : T
5151

5252
const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2")});
53-
>noError : void
54-
>ensureNoDuplicates({main: value("test"), alternate: value("test2")}) : void
55-
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
53+
>noError : any
54+
>ensureNoDuplicates({main: value("test"), alternate: value("test2")}) : any
55+
>ensureNoDuplicates : <T extends { [K in keyof T]: never; }>(vals: T) => void
5656
>{main: value("test"), alternate: value("test2")} : { main: Record<"val", "test">; alternate: Record<"val", "test2">; }
5757
>main : Record<"val", "test">
5858
>value("test") : Record<"val", "test">
@@ -64,9 +64,9 @@ const noError = ensureNoDuplicates({main: value("test"), alternate: value("test2
6464
>"test2" : "test2"
6565

6666
const shouldBeNoError = ensureNoDuplicates({main: value("test")});
67-
>shouldBeNoError : void
68-
>ensureNoDuplicates({main: value("test")}) : void
69-
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
67+
>shouldBeNoError : any
68+
>ensureNoDuplicates({main: value("test")}) : any
69+
>ensureNoDuplicates : <T extends { [K in keyof T]: never; }>(vals: T) => void
7070
>{main: value("test")} : { main: Record<"val", "test">; }
7171
>main : Record<"val", "test">
7272
>value("test") : Record<"val", "test">
@@ -76,7 +76,7 @@ const shouldBeNoError = ensureNoDuplicates({main: value("test")});
7676
const shouldBeError = ensureNoDuplicates({main: value("dup"), alternate: value("dup")});
7777
>shouldBeError : any
7878
>ensureNoDuplicates({main: value("dup"), alternate: value("dup")}) : any
79-
>ensureNoDuplicates : <T extends { [K in keyof T]: Extract<T[K], Record<"val", string>>["val"] extends Extract<T[Exclude<keyof T, K>], Record<"val", string>>["val"] ? never : any; }>(vals: T) => void
79+
>ensureNoDuplicates : <T extends { [K in keyof T]: never; }>(vals: T) => void
8080
>{main: value("dup"), alternate: value("dup")} : { main: Record<"val", "dup">; alternate: Record<"val", "dup">; }
8181
>main : Record<"val", "dup">
8282
>value("dup") : Record<"val", "dup">

0 commit comments

Comments
 (0)