Skip to content

Commit eafff75

Browse files
authored
Remove diagnostic dependent output in structuredTypeRelatedTo (#29817)
* Unify variance probing error exceptions between interfaces/aliases * Consistiently return false on variance probe failure * Remove strictFunctionTypes early bail from getVariances so independent type parameters are correctly measured * Fix lint, remove now-redundant change from covariant void check function
1 parent 9d9cfaf commit eafff75

19 files changed

+1564
-107
lines changed

src/compiler/checker.ts

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -12586,6 +12586,7 @@ namespace ts {
1258612586

1258712587
let result: Ternary;
1258812588
let originalErrorInfo: DiagnosticMessageChain | undefined;
12589+
let varianceCheckFailed = false;
1258912590
const saveErrorInfo = errorInfo;
1259012591

1259112592
// We limit alias variance probing to only object and conditional types since their alias behavior
@@ -12595,11 +12596,10 @@ namespace ts {
1259512596
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol &&
1259612597
!(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) {
1259712598
const variances = getAliasVariances(source.aliasSymbol);
12598-
if (result = typeArgumentsRelatedTo(source.aliasTypeArguments, target.aliasTypeArguments, variances, reportErrors)) {
12599-
return result;
12599+
const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances);
12600+
if (varianceResult !== undefined) {
12601+
return varianceResult;
1260012602
}
12601-
originalErrorInfo = errorInfo;
12602-
errorInfo = saveErrorInfo;
1260312603
}
1260412604

1260512605
if (target.flags & TypeFlags.TypeParameter) {
@@ -12764,31 +12764,9 @@ namespace ts {
1276412764
// type references (which are intended by be compared structurally). Obtain the variance
1276512765
// information for the type parameters and relate the type arguments accordingly.
1276612766
const variances = getVariances((<TypeReference>source).target);
12767-
if (result = typeArgumentsRelatedTo((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, variances, reportErrors)) {
12768-
return result;
12769-
}
12770-
// The type arguments did not relate appropriately, but it may be because we have no variance
12771-
// information (in which case typeArgumentsRelatedTo defaulted to covariance for all type
12772-
// arguments). It might also be the case that the target type has a 'void' type argument for
12773-
// a covariant type parameter that is only used in return positions within the generic type
12774-
// (in which case any type argument is permitted on the source side). In those cases we proceed
12775-
// with a structural comparison. Otherwise, we know for certain the instantiations aren't
12776-
// related and we can return here.
12777-
if (variances !== emptyArray && !hasCovariantVoidArgument(<TypeReference>target, variances)) {
12778-
// In some cases generic types that are covariant in regular type checking mode become
12779-
// invariant in --strictFunctionTypes mode because one or more type parameters are used in
12780-
// both co- and contravariant positions. In order to make it easier to diagnose *why* such
12781-
// types are invariant, if any of the type parameters are invariant we reset the reported
12782-
// errors and instead force a structural comparison (which will include elaborations that
12783-
// reveal the reason).
12784-
if (!(reportErrors && some(variances, v => v === Variance.Invariant))) {
12785-
return Ternary.False;
12786-
}
12787-
// We remember the original error information so we can restore it in case the structural
12788-
// comparison unexpectedly succeeds. This can happen when the structural comparison result
12789-
// is a Ternary.Maybe for example caused by the recursion depth limiter.
12790-
originalErrorInfo = errorInfo;
12791-
errorInfo = saveErrorInfo;
12767+
const varianceResult = relateVariances((<TypeReference>source).typeArguments, (<TypeReference>target).typeArguments, variances);
12768+
if (varianceResult !== undefined) {
12769+
return varianceResult;
1279212770
}
1279312771
}
1279412772
else if (isReadonlyArrayType(target) ? isArrayType(source) || isTupleType(source) : isArrayType(target) && isTupleType(source) && !source.target.readonly) {
@@ -12815,16 +12793,48 @@ namespace ts {
1281512793
}
1281612794
}
1281712795
}
12818-
if (result) {
12819-
if (!originalErrorInfo) {
12820-
errorInfo = saveErrorInfo;
12821-
return result;
12822-
}
12823-
errorInfo = originalErrorInfo;
12796+
if (varianceCheckFailed && result) {
12797+
errorInfo = originalErrorInfo || errorInfo || saveErrorInfo; // Use variance error (there is no structural one) and return false
12798+
}
12799+
else if (result) {
12800+
return result;
1282412801
}
1282512802
}
1282612803
}
1282712804
return Ternary.False;
12805+
12806+
function relateVariances(sourceTypeArguments: ReadonlyArray<Type> | undefined, targetTypeArguments: ReadonlyArray<Type> | undefined, variances: Variance[]) {
12807+
if (result = typeArgumentsRelatedTo(sourceTypeArguments, targetTypeArguments, variances, reportErrors)) {
12808+
return result;
12809+
}
12810+
const isCovariantVoid = targetTypeArguments && hasCovariantVoidArgument(targetTypeArguments, variances);
12811+
varianceCheckFailed = !isCovariantVoid;
12812+
// The type arguments did not relate appropriately, but it may be because we have no variance
12813+
// information (in which case typeArgumentsRelatedTo defaulted to covariance for all type
12814+
// arguments). It might also be the case that the target type has a 'void' type argument for
12815+
// a covariant type parameter that is only used in return positions within the generic type
12816+
// (in which case any type argument is permitted on the source side). In those cases we proceed
12817+
// with a structural comparison. Otherwise, we know for certain the instantiations aren't
12818+
// related and we can return here.
12819+
if (variances !== emptyArray && !isCovariantVoid) {
12820+
// In some cases generic types that are covariant in regular type checking mode become
12821+
// invariant in --strictFunctionTypes mode because one or more type parameters are used in
12822+
// both co- and contravariant positions. In order to make it easier to diagnose *why* such
12823+
// types are invariant, if any of the type parameters are invariant we reset the reported
12824+
// errors and instead force a structural comparison (which will include elaborations that
12825+
// reveal the reason).
12826+
// We can switch on `reportErrors` here, since varianceCheckFailed guarantees we return `False`,
12827+
// we can return `False` early here to skip calculating the structural error message we don't need.
12828+
if (varianceCheckFailed && !(reportErrors && some(variances, v => v === Variance.Invariant))) {
12829+
return Ternary.False;
12830+
}
12831+
// We remember the original error information so we can restore it in case the structural
12832+
// comparison unexpectedly succeeds. This can happen when the structural comparison result
12833+
// is a Ternary.Maybe for example caused by the recursion depth limiter.
12834+
originalErrorInfo = errorInfo;
12835+
errorInfo = saveErrorInfo;
12836+
}
12837+
}
1282812838
}
1282912839

1283012840
// A type [P in S]: X is related to a type [Q in T]: Y if T is related to S and X' is
@@ -13333,17 +13343,17 @@ namespace ts {
1333313343

1333413344
function getVariances(type: GenericType): Variance[] {
1333513345
// Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters)
13336-
if (!strictFunctionTypes || type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
13346+
if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
1333713347
return emptyArray;
1333813348
}
1333913349
return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference);
1334013350
}
1334113351

1334213352
// Return true if the given type reference has a 'void' type argument for a covariant type parameter.
1334313353
// See comment at call in recursiveTypeRelatedTo for when this case matters.
13344-
function hasCovariantVoidArgument(type: TypeReference, variances: Variance[]): boolean {
13354+
function hasCovariantVoidArgument(typeArguments: ReadonlyArray<Type>, variances: Variance[]): boolean {
1334513355
for (let i = 0; i < variances.length; i++) {
13346-
if (variances[i] === Variance.Covariant && type.typeArguments![i].flags & TypeFlags.Void) {
13356+
if (variances[i] === Variance.Covariant && typeArguments[i].flags & TypeFlags.Void) {
1334713357
return true;
1334813358
}
1334913359
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
tests/cases/compiler/checkInfiniteExpansionTermination.ts(16,1): error TS2322: Type 'ISubject<Bar>' is not assignable to type 'IObservable<Foo>'.
2+
Types of property 'n' are incompatible.
3+
Type 'IObservable<Bar[]>' is not assignable to type 'IObservable<Foo[]>'.
4+
Type 'Bar[]' is not assignable to type 'Foo[]'.
5+
Property 'x' is missing in type 'Bar' but required in type 'Foo'.
6+
7+
8+
==== tests/cases/compiler/checkInfiniteExpansionTermination.ts (1 errors) ====
9+
// Regression test for #1002
10+
// Before fix this code would cause infinite loop
11+
12+
interface IObservable<T> {
13+
n: IObservable<T[]>; // Needed, must be T[]
14+
}
15+
16+
// Needed
17+
interface ISubject<T> extends IObservable<T> { }
18+
19+
interface Foo { x }
20+
interface Bar { y }
21+
22+
var values: IObservable<Foo>;
23+
var values2: ISubject<Bar>;
24+
values = values2;
25+
~~~~~~
26+
!!! error TS2322: Type 'ISubject<Bar>' is not assignable to type 'IObservable<Foo>'.
27+
!!! error TS2322: Types of property 'n' are incompatible.
28+
!!! error TS2322: Type 'IObservable<Bar[]>' is not assignable to type 'IObservable<Foo[]>'.
29+
!!! error TS2322: Type 'Bar[]' is not assignable to type 'Foo[]'.
30+
!!! error TS2322: Property 'x' is missing in type 'Bar' but required in type 'Foo'.
31+
!!! related TS2728 tests/cases/compiler/checkInfiniteExpansionTermination.ts:11:17: 'x' is declared here.
32+

tests/baselines/reference/complexRecursiveCollections.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1137,7 +1137,7 @@ declare module Immutable {
11371137
>Seq : typeof Seq
11381138

11391139
function isSeq(maybeSeq: any): maybeSeq is Seq.Indexed<any> | Seq.Keyed<any, any>;
1140-
>isSeq : (maybeSeq: any) => maybeSeq is Keyed<any, any> | Indexed<any>
1140+
>isSeq : (maybeSeq: any) => maybeSeq is Indexed<any> | Keyed<any, any>
11411141
>maybeSeq : any
11421142
>Seq : any
11431143
>Seq : any

tests/baselines/reference/conditionalTypes1.errors.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(103,5): error TS2
1717
tests/cases/conformance/types/conditional/conditionalTypes1.ts(104,5): error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'T'.
1818
tests/cases/conformance/types/conditional/conditionalTypes1.ts(106,5): error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>'.
1919
Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
20+
Type 'keyof T' is not assignable to type 'never'.
21+
Type 'string | number | symbol' is not assignable to type 'never'.
22+
Type 'string' is not assignable to type 'never'.
2023
tests/cases/conformance/types/conditional/conditionalTypes1.ts(108,5): error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>'.
2124
Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
25+
Type 'keyof T' is not assignable to type 'never'.
2226
tests/cases/conformance/types/conditional/conditionalTypes1.ts(114,5): error TS2322: Type 'keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
2327
Type 'string | number | symbol' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
2428
Type 'string' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
@@ -183,11 +187,15 @@ tests/cases/conformance/types/conditional/conditionalTypes1.ts(288,43): error TS
183187
~
184188
!!! error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>'.
185189
!!! error TS2322: Type 'T[keyof T] extends Function ? keyof T : never' is not assignable to type 'T[keyof T] extends Function ? never : keyof T'.
190+
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
191+
!!! error TS2322: Type 'string | number | symbol' is not assignable to type 'never'.
192+
!!! error TS2322: Type 'string' is not assignable to type 'never'.
186193
z = x;
187194
z = y; // Error
188195
~
189196
!!! error TS2322: Type 'Pick<T, { [K in keyof T]: T[K] extends Function ? K : never; }[keyof T]>' is not assignable to type 'Pick<T, { [K in keyof T]: T[K] extends Function ? never : K; }[keyof T]>'.
190197
!!! error TS2322: Type 'T[keyof T] extends Function ? never : keyof T' is not assignable to type 'T[keyof T] extends Function ? keyof T : never'.
198+
!!! error TS2322: Type 'keyof T' is not assignable to type 'never'.
191199
}
192200

193201
function f8<T>(x: keyof T, y: FunctionPropertyNames<T>, z: NonFunctionPropertyNames<T>) {

tests/baselines/reference/mappedTypeRelationships.errors.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(66,5): error TS2
3434
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(66,5): error TS2542: Index signature in type 'Readonly<U>' only permits reading.
3535
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(72,5): error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
3636
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(78,5): error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
37+
Type 'Thing' is not assignable to type 'T'.
3738
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(88,5): error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
39+
Type 'Thing' is not assignable to type 'T'.
3840
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(127,5): error TS2322: Type 'Partial<U>' is not assignable to type 'Identity<U>'.
3941
tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(143,5): error TS2322: Type '{ [P in keyof T]: T[P]; }' is not assignable to type '{ [P in keyof T]: U[P]; }'.
4042
Type 'T[P]' is not assignable to type 'U[P]'.
@@ -197,6 +199,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
197199
y = x; // Error
198200
~
199201
!!! error TS2322: Type 'Partial<Thing>' is not assignable to type 'Partial<T>'.
202+
!!! error TS2322: Type 'Thing' is not assignable to type 'T'.
200203
}
201204

202205
function f40<T>(x: T, y: Readonly<T>) {
@@ -209,6 +212,7 @@ tests/cases/conformance/types/mapped/mappedTypeRelationships.ts(168,5): error TS
209212
y = x; // Error
210213
~
211214
!!! error TS2322: Type 'Readonly<Thing>' is not assignable to type 'Readonly<T>'.
215+
!!! error TS2322: Type 'Thing' is not assignable to type 'T'.
212216
}
213217

214218
type Item = {

tests/baselines/reference/mappedTypes5.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
tests/cases/conformance/types/mapped/mappedTypes5.ts(6,9): error TS2322: Type 'Partial<T>' is not assignable to type 'Readonly<T>'.
22
tests/cases/conformance/types/mapped/mappedTypes5.ts(8,9): error TS2322: Type 'Partial<Readonly<T>>' is not assignable to type 'Readonly<T>'.
33
tests/cases/conformance/types/mapped/mappedTypes5.ts(9,9): error TS2322: Type 'Readonly<Partial<T>>' is not assignable to type 'Readonly<T>'.
4+
Type 'Partial<T>' is not assignable to type 'T'.
45

56

67
==== tests/cases/conformance/types/mapped/mappedTypes5.ts (3 errors) ====
@@ -19,6 +20,7 @@ tests/cases/conformance/types/mapped/mappedTypes5.ts(9,9): error TS2322: Type 'R
1920
let b4: Readonly<T> = rp; // Error
2021
~~
2122
!!! error TS2322: Type 'Readonly<Partial<T>>' is not assignable to type 'Readonly<T>'.
23+
!!! error TS2322: Type 'Partial<T>' is not assignable to type 'T'.
2224
let c1: Partial<Readonly<T>> = p;
2325
let c2: Partial<Readonly<T>> = r;
2426
let c3: Partial<Readonly<T>> = pr;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
tests/cases/compiler/recursiveTypeComparison.ts(14,5): error TS2322: Type 'Observable<{}>' is not assignable to type 'Property<number>'.
2+
Types of property 'needThisOne' are incompatible.
3+
Type 'Observable<{}>' is not assignable to type 'Observable<number>'.
4+
Type '{}' is not assignable to type 'number'.
5+
6+
7+
==== tests/cases/compiler/recursiveTypeComparison.ts (1 errors) ====
8+
// Before fix this would take an exceeding long time to complete (#1170)
9+
10+
interface Observable<T> {
11+
// This member can't be of type T, Property<T>, or Observable<anything but T>
12+
needThisOne: Observable<T>;
13+
// Add more to make it slower
14+
expo1: Property<T[]>; // 0.31 seconds in check
15+
expo2: Property<T[]>; // 3.11 seconds
16+
expo3: Property<T[]>; // 82.28 seconds
17+
}
18+
interface Property<T> extends Observable<T> { }
19+
20+
var p: Observable<{}>;
21+
var stuck: Property<number> = p;
22+
~~~~~
23+
!!! error TS2322: Type 'Observable<{}>' is not assignable to type 'Property<number>'.
24+
!!! error TS2322: Types of property 'needThisOne' are incompatible.
25+
!!! error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<number>'.
26+
!!! error TS2322: Type '{}' is not assignable to type 'number'.
27+

0 commit comments

Comments
 (0)