Improve variance measurement (#36261)

* No covariance default for recursive references in variance measurement

* Add tests

* Accept new baselines
This commit is contained in:
Anders Hejlsberg
2020-01-17 11:50:38 -08:00
committed by GitHub
parent e2e1f6fd85
commit 1a10e712e6
8 changed files with 600 additions and 63 deletions

View File

@@ -306,6 +306,7 @@ namespace ts {
const emptySymbols = createSymbolTable();
const identityMapper: (type: Type) => Type = identity;
const arrayVariances = [VarianceFlags.Covariant];
const compilerOptions = host.getCompilerOptions();
const languageVersion = getEmitScriptTarget(compilerOptions);
@@ -15406,6 +15407,9 @@ namespace ts {
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol &&
!(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) {
const variances = getAliasVariances(source.aliasSymbol);
if (variances === emptyArray) {
return Ternary.Maybe;
}
const varianceResult = relateVariances(source.aliasTypeArguments, target.aliasTypeArguments, variances, intersectionState);
if (varianceResult !== undefined) {
return varianceResult;
@@ -15602,6 +15606,12 @@ namespace ts {
// type references (which are intended by be compared structurally). Obtain the variance
// information for the type parameters and relate the type arguments accordingly.
const variances = getVariances((<TypeReference>source).target);
// We return Ternary.Maybe for a recursive invocation of getVariances (signalled by emptyArray). This
// effectively means we measure variance only from type parameter occurrences that aren't nested in
// recursive instantiations of the generic type.
if (variances === emptyArray) {
return Ternary.Maybe;
}
const varianceResult = relateVariances(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), variances, intersectionState);
if (varianceResult !== undefined) {
return varianceResult;
@@ -16421,8 +16431,7 @@ namespace ts {
// a digest of the type comparisons that occur for each type argument when instantiations of the
// generic type are structurally compared. We infer the variance information by comparing
// instantiations of the generic type for type arguments with known relations. The function
// returns the emptyArray singleton if we're not in strictFunctionTypes mode or if the function
// has been invoked recursively for the given generic type.
// returns the emptyArray singleton when invoked recursively for the given generic type.
function getVariancesWorker<TCache extends { variances?: VarianceFlags[] }>(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] {
let variances = cache.variances;
if (!variances) {
@@ -16465,9 +16474,9 @@ namespace ts {
}
function getVariances(type: GenericType): VarianceFlags[] {
// Arrays and tuples are known to be covariant, no need to spend time computing this (emptyArray implies covariance for all parameters)
// Arrays and tuples are known to be covariant, no need to spend time computing this.
if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
return emptyArray;
return arrayVariances;
}
return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference);
}

View File

@@ -1,32 +0,0 @@
tests/cases/compiler/checkInfiniteExpansionTermination.ts(16,1): error TS2322: Type 'ISubject<Bar>' is not assignable to type 'IObservable<Foo>'.
Types of property 'n' are incompatible.
Type 'IObservable<Bar[]>' is not assignable to type 'IObservable<Foo[]>'.
Type 'Bar[]' is not assignable to type 'Foo[]'.
Property 'x' is missing in type 'Bar' but required in type 'Foo'.
==== tests/cases/compiler/checkInfiniteExpansionTermination.ts (1 errors) ====
// Regression test for #1002
// Before fix this code would cause infinite loop
interface IObservable<T> {
n: IObservable<T[]>; // Needed, must be T[]
}
// Needed
interface ISubject<T> extends IObservable<T> { }
interface Foo { x }
interface Bar { y }
var values: IObservable<Foo>;
var values2: ISubject<Bar>;
values = values2;
~~~~~~
!!! error TS2322: Type 'ISubject<Bar>' is not assignable to type 'IObservable<Foo>'.
!!! error TS2322: Types of property 'n' are incompatible.
!!! error TS2322: Type 'IObservable<Bar[]>' is not assignable to type 'IObservable<Foo[]>'.
!!! error TS2322: Type 'Bar[]' is not assignable to type 'Foo[]'.
!!! error TS2322: Property 'x' is missing in type 'Bar' but required in type 'Foo'.
!!! related TS2728 tests/cases/compiler/checkInfiniteExpansionTermination.ts:11:17: 'x' is declared here.

View File

@@ -1,27 +0,0 @@
tests/cases/compiler/recursiveTypeComparison.ts(14,5): error TS2322: Type 'Observable<{}>' is not assignable to type 'Property<number>'.
Types of property 'needThisOne' are incompatible.
Type 'Observable<{}>' is not assignable to type 'Observable<number>'.
Type '{}' is not assignable to type 'number'.
==== tests/cases/compiler/recursiveTypeComparison.ts (1 errors) ====
// Before fix this would take an exceeding long time to complete (#1170)
interface Observable<T> {
// This member can't be of type T, Property<T>, or Observable<anything but T>
needThisOne: Observable<T>;
// Add more to make it slower
expo1: Property<T[]>; // 0.31 seconds in check
expo2: Property<T[]>; // 3.11 seconds
expo3: Property<T[]>; // 82.28 seconds
}
interface Property<T> extends Observable<T> { }
var p: Observable<{}>;
var stuck: Property<number> = p;
~~~~~
!!! error TS2322: Type 'Observable<{}>' is not assignable to type 'Property<number>'.
!!! error TS2322: Types of property 'needThisOne' are incompatible.
!!! error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<number>'.
!!! error TS2322: Type '{}' is not assignable to type 'number'.

View File

@@ -0,0 +1,122 @@
tests/cases/compiler/varianceMeasurement.ts(10,7): error TS2322: Type 'Foo1<string>' is not assignable to type 'Foo1<"a">'.
Type 'string' is not assignable to type '"a"'.
tests/cases/compiler/varianceMeasurement.ts(21,7): error TS2322: Type 'Foo2<string>' is not assignable to type 'Foo2<"a">'.
Types of property 'x' are incompatible.
Type 'string' is not assignable to type '"a"'.
tests/cases/compiler/varianceMeasurement.ts(22,7): error TS2322: Type 'Foo2<string>' is not assignable to type 'Foo2<unknown>'.
The types of 'y.x' are incompatible between these types.
Type '(arg: string) => void' is not assignable to type '(arg: unknown) => void'.
Types of parameters 'arg' and 'arg' are incompatible.
Type 'unknown' is not assignable to type 'string'.
tests/cases/compiler/varianceMeasurement.ts(33,7): error TS2322: Type 'Foo3<string>' is not assignable to type 'Foo3<"a">'.
Type 'string' is not assignable to type '"a"'.
tests/cases/compiler/varianceMeasurement.ts(44,7): error TS2322: Type 'Foo4<string>' is not assignable to type 'Foo4<"a">'.
Types of property 'x' are incompatible.
Type 'string' is not assignable to type '"a"'.
tests/cases/compiler/varianceMeasurement.ts(45,7): error TS2322: Type 'Foo4<string>' is not assignable to type 'Foo4<unknown>'.
The types of 'y.x' are incompatible between these types.
Type '(arg: string) => void' is not assignable to type '(arg: unknown) => void'.
Types of parameters 'arg' and 'arg' are incompatible.
Type 'unknown' is not assignable to type 'string'.
tests/cases/compiler/varianceMeasurement.ts(57,7): error TS2322: Type 'Fn<string, number>' is not assignable to type 'Fn<unknown, number>'.
Type 'unknown' is not assignable to type 'string'.
tests/cases/compiler/varianceMeasurement.ts(62,7): error TS2322: Type 'Fn<string, number>' is not assignable to type 'Fn<string, 0>'.
Type 'number' is not assignable to type '0'.
==== tests/cases/compiler/varianceMeasurement.ts (8 errors) ====
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
interface Foo1<T> {
x: T;
y: Foo1<(arg: T) => void>;
}
declare const f10: Foo1<string>;
const f11: Foo1<'a'> = f10;
~~~
!!! error TS2322: Type 'Foo1<string>' is not assignable to type 'Foo1<"a">'.
!!! error TS2322: Type 'string' is not assignable to type '"a"'.
const f12: Foo1<unknown> = f10;
// The type below is invariant in T and is measured as such.
interface Foo2<T> {
x: T;
y: { x: (arg: T) => void, y: Foo2<(arg: T) => void>; }
}
declare const f20: Foo2<string>;
const f21: Foo2<'a'> = f20;
~~~
!!! error TS2322: Type 'Foo2<string>' is not assignable to type 'Foo2<"a">'.
!!! error TS2322: Types of property 'x' are incompatible.
!!! error TS2322: Type 'string' is not assignable to type '"a"'.
const f22: Foo2<unknown> = f20;
~~~
!!! error TS2322: Type 'Foo2<string>' is not assignable to type 'Foo2<unknown>'.
!!! error TS2322: The types of 'y.x' are incompatible between these types.
!!! error TS2322: Type '(arg: string) => void' is not assignable to type '(arg: unknown) => void'.
!!! error TS2322: Types of parameters 'arg' and 'arg' are incompatible.
!!! error TS2322: Type 'unknown' is not assignable to type 'string'.
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
type Foo3<T> = {
x: T;
y: Foo3<(arg: T) => void>;
}
declare const f30: Foo3<string>;
const f31: Foo3<'a'> = f30;
~~~
!!! error TS2322: Type 'Foo3<string>' is not assignable to type 'Foo3<"a">'.
!!! error TS2322: Type 'string' is not assignable to type '"a"'.
const f32: Foo3<unknown> = f30;
// The type below is invariant in T and is measured as such.
type Foo4<T> = {
x: T;
y: { x: (arg: T) => void, y: Foo4<(arg: T) => void>; }
}
declare const f40: Foo4<string>;
const f41: Foo4<'a'> = f40;
~~~
!!! error TS2322: Type 'Foo4<string>' is not assignable to type 'Foo4<"a">'.
!!! error TS2322: Types of property 'x' are incompatible.
!!! error TS2322: Type 'string' is not assignable to type '"a"'.
const f42: Foo4<unknown> = f40;
~~~
!!! error TS2322: Type 'Foo4<string>' is not assignable to type 'Foo4<unknown>'.
!!! error TS2322: The types of 'y.x' are incompatible between these types.
!!! error TS2322: Type '(arg: string) => void' is not assignable to type '(arg: unknown) => void'.
!!! error TS2322: Types of parameters 'arg' and 'arg' are incompatible.
!!! error TS2322: Type 'unknown' is not assignable to type 'string'.
// Repro from #3580
interface Fn<A, B> {
(a: A): B;
then<C>(next: Fn<B, C>): Fn<A, C>;
}
declare const fn: Fn<string, number>;
// Contravariant in A
const fn1: Fn<unknown, number> = fn; // Error
~~~
!!! error TS2322: Type 'Fn<string, number>' is not assignable to type 'Fn<unknown, number>'.
!!! error TS2322: Type 'unknown' is not assignable to type 'string'.
const fn2: Fn<'a', number> = fn;
// Covariant in B
const fn3: Fn<string, unknown> = fn;
const fn4: Fn<string, 0> = fn; // Error
~~~
!!! error TS2322: Type 'Fn<string, number>' is not assignable to type 'Fn<string, 0>'.
!!! error TS2322: Type 'number' is not assignable to type '0'.

View File

@@ -0,0 +1,83 @@
//// [varianceMeasurement.ts]
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
interface Foo1<T> {
x: T;
y: Foo1<(arg: T) => void>;
}
declare const f10: Foo1<string>;
const f11: Foo1<'a'> = f10;
const f12: Foo1<unknown> = f10;
// The type below is invariant in T and is measured as such.
interface Foo2<T> {
x: T;
y: { x: (arg: T) => void, y: Foo2<(arg: T) => void>; }
}
declare const f20: Foo2<string>;
const f21: Foo2<'a'> = f20;
const f22: Foo2<unknown> = f20;
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
type Foo3<T> = {
x: T;
y: Foo3<(arg: T) => void>;
}
declare const f30: Foo3<string>;
const f31: Foo3<'a'> = f30;
const f32: Foo3<unknown> = f30;
// The type below is invariant in T and is measured as such.
type Foo4<T> = {
x: T;
y: { x: (arg: T) => void, y: Foo4<(arg: T) => void>; }
}
declare const f40: Foo4<string>;
const f41: Foo4<'a'> = f40;
const f42: Foo4<unknown> = f40;
// Repro from #3580
interface Fn<A, B> {
(a: A): B;
then<C>(next: Fn<B, C>): Fn<A, C>;
}
declare const fn: Fn<string, number>;
// Contravariant in A
const fn1: Fn<unknown, number> = fn; // Error
const fn2: Fn<'a', number> = fn;
// Covariant in B
const fn3: Fn<string, unknown> = fn;
const fn4: Fn<string, 0> = fn; // Error
//// [varianceMeasurement.js]
"use strict";
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
var f11 = f10;
var f12 = f10;
var f21 = f20;
var f22 = f20;
var f31 = f30;
var f32 = f30;
var f41 = f40;
var f42 = f40;
// Contravariant in A
var fn1 = fn; // Error
var fn2 = fn;
// Covariant in B
var fn3 = fn;
var fn4 = fn; // Error

View File

@@ -0,0 +1,185 @@
=== tests/cases/compiler/varianceMeasurement.ts ===
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
interface Foo1<T> {
>Foo1 : Symbol(Foo1, Decl(varianceMeasurement.ts, 0, 0))
>T : Symbol(T, Decl(varianceMeasurement.ts, 3, 15))
x: T;
>x : Symbol(Foo1.x, Decl(varianceMeasurement.ts, 3, 19))
>T : Symbol(T, Decl(varianceMeasurement.ts, 3, 15))
y: Foo1<(arg: T) => void>;
>y : Symbol(Foo1.y, Decl(varianceMeasurement.ts, 4, 7))
>Foo1 : Symbol(Foo1, Decl(varianceMeasurement.ts, 0, 0))
>arg : Symbol(arg, Decl(varianceMeasurement.ts, 5, 11))
>T : Symbol(T, Decl(varianceMeasurement.ts, 3, 15))
}
declare const f10: Foo1<string>;
>f10 : Symbol(f10, Decl(varianceMeasurement.ts, 8, 13))
>Foo1 : Symbol(Foo1, Decl(varianceMeasurement.ts, 0, 0))
const f11: Foo1<'a'> = f10;
>f11 : Symbol(f11, Decl(varianceMeasurement.ts, 9, 5))
>Foo1 : Symbol(Foo1, Decl(varianceMeasurement.ts, 0, 0))
>f10 : Symbol(f10, Decl(varianceMeasurement.ts, 8, 13))
const f12: Foo1<unknown> = f10;
>f12 : Symbol(f12, Decl(varianceMeasurement.ts, 10, 5))
>Foo1 : Symbol(Foo1, Decl(varianceMeasurement.ts, 0, 0))
>f10 : Symbol(f10, Decl(varianceMeasurement.ts, 8, 13))
// The type below is invariant in T and is measured as such.
interface Foo2<T> {
>Foo2 : Symbol(Foo2, Decl(varianceMeasurement.ts, 10, 31))
>T : Symbol(T, Decl(varianceMeasurement.ts, 14, 15))
x: T;
>x : Symbol(Foo2.x, Decl(varianceMeasurement.ts, 14, 19))
>T : Symbol(T, Decl(varianceMeasurement.ts, 14, 15))
y: { x: (arg: T) => void, y: Foo2<(arg: T) => void>; }
>y : Symbol(Foo2.y, Decl(varianceMeasurement.ts, 15, 7))
>x : Symbol(x, Decl(varianceMeasurement.ts, 16, 6))
>arg : Symbol(arg, Decl(varianceMeasurement.ts, 16, 11))
>T : Symbol(T, Decl(varianceMeasurement.ts, 14, 15))
>y : Symbol(y, Decl(varianceMeasurement.ts, 16, 27))
>Foo2 : Symbol(Foo2, Decl(varianceMeasurement.ts, 10, 31))
>arg : Symbol(arg, Decl(varianceMeasurement.ts, 16, 37))
>T : Symbol(T, Decl(varianceMeasurement.ts, 14, 15))
}
declare const f20: Foo2<string>;
>f20 : Symbol(f20, Decl(varianceMeasurement.ts, 19, 13))
>Foo2 : Symbol(Foo2, Decl(varianceMeasurement.ts, 10, 31))
const f21: Foo2<'a'> = f20;
>f21 : Symbol(f21, Decl(varianceMeasurement.ts, 20, 5))
>Foo2 : Symbol(Foo2, Decl(varianceMeasurement.ts, 10, 31))
>f20 : Symbol(f20, Decl(varianceMeasurement.ts, 19, 13))
const f22: Foo2<unknown> = f20;
>f22 : Symbol(f22, Decl(varianceMeasurement.ts, 21, 5))
>Foo2 : Symbol(Foo2, Decl(varianceMeasurement.ts, 10, 31))
>f20 : Symbol(f20, Decl(varianceMeasurement.ts, 19, 13))
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
type Foo3<T> = {
>Foo3 : Symbol(Foo3, Decl(varianceMeasurement.ts, 21, 31))
>T : Symbol(T, Decl(varianceMeasurement.ts, 26, 10))
x: T;
>x : Symbol(x, Decl(varianceMeasurement.ts, 26, 16))
>T : Symbol(T, Decl(varianceMeasurement.ts, 26, 10))
y: Foo3<(arg: T) => void>;
>y : Symbol(y, Decl(varianceMeasurement.ts, 27, 7))
>Foo3 : Symbol(Foo3, Decl(varianceMeasurement.ts, 21, 31))
>arg : Symbol(arg, Decl(varianceMeasurement.ts, 28, 11))
>T : Symbol(T, Decl(varianceMeasurement.ts, 26, 10))
}
declare const f30: Foo3<string>;
>f30 : Symbol(f30, Decl(varianceMeasurement.ts, 31, 13))
>Foo3 : Symbol(Foo3, Decl(varianceMeasurement.ts, 21, 31))
const f31: Foo3<'a'> = f30;
>f31 : Symbol(f31, Decl(varianceMeasurement.ts, 32, 5))
>Foo3 : Symbol(Foo3, Decl(varianceMeasurement.ts, 21, 31))
>f30 : Symbol(f30, Decl(varianceMeasurement.ts, 31, 13))
const f32: Foo3<unknown> = f30;
>f32 : Symbol(f32, Decl(varianceMeasurement.ts, 33, 5))
>Foo3 : Symbol(Foo3, Decl(varianceMeasurement.ts, 21, 31))
>f30 : Symbol(f30, Decl(varianceMeasurement.ts, 31, 13))
// The type below is invariant in T and is measured as such.
type Foo4<T> = {
>Foo4 : Symbol(Foo4, Decl(varianceMeasurement.ts, 33, 31))
>T : Symbol(T, Decl(varianceMeasurement.ts, 37, 10))
x: T;
>x : Symbol(x, Decl(varianceMeasurement.ts, 37, 16))
>T : Symbol(T, Decl(varianceMeasurement.ts, 37, 10))
y: { x: (arg: T) => void, y: Foo4<(arg: T) => void>; }
>y : Symbol(y, Decl(varianceMeasurement.ts, 38, 7))
>x : Symbol(x, Decl(varianceMeasurement.ts, 39, 6))
>arg : Symbol(arg, Decl(varianceMeasurement.ts, 39, 11))
>T : Symbol(T, Decl(varianceMeasurement.ts, 37, 10))
>y : Symbol(y, Decl(varianceMeasurement.ts, 39, 27))
>Foo4 : Symbol(Foo4, Decl(varianceMeasurement.ts, 33, 31))
>arg : Symbol(arg, Decl(varianceMeasurement.ts, 39, 37))
>T : Symbol(T, Decl(varianceMeasurement.ts, 37, 10))
}
declare const f40: Foo4<string>;
>f40 : Symbol(f40, Decl(varianceMeasurement.ts, 42, 13))
>Foo4 : Symbol(Foo4, Decl(varianceMeasurement.ts, 33, 31))
const f41: Foo4<'a'> = f40;
>f41 : Symbol(f41, Decl(varianceMeasurement.ts, 43, 5))
>Foo4 : Symbol(Foo4, Decl(varianceMeasurement.ts, 33, 31))
>f40 : Symbol(f40, Decl(varianceMeasurement.ts, 42, 13))
const f42: Foo4<unknown> = f40;
>f42 : Symbol(f42, Decl(varianceMeasurement.ts, 44, 5))
>Foo4 : Symbol(Foo4, Decl(varianceMeasurement.ts, 33, 31))
>f40 : Symbol(f40, Decl(varianceMeasurement.ts, 42, 13))
// Repro from #3580
interface Fn<A, B> {
>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31))
>A : Symbol(A, Decl(varianceMeasurement.ts, 48, 13))
>B : Symbol(B, Decl(varianceMeasurement.ts, 48, 15))
(a: A): B;
>a : Symbol(a, Decl(varianceMeasurement.ts, 49, 3))
>A : Symbol(A, Decl(varianceMeasurement.ts, 48, 13))
>B : Symbol(B, Decl(varianceMeasurement.ts, 48, 15))
then<C>(next: Fn<B, C>): Fn<A, C>;
>then : Symbol(Fn.then, Decl(varianceMeasurement.ts, 49, 12))
>C : Symbol(C, Decl(varianceMeasurement.ts, 50, 7))
>next : Symbol(next, Decl(varianceMeasurement.ts, 50, 10))
>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31))
>B : Symbol(B, Decl(varianceMeasurement.ts, 48, 15))
>C : Symbol(C, Decl(varianceMeasurement.ts, 50, 7))
>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31))
>A : Symbol(A, Decl(varianceMeasurement.ts, 48, 13))
>C : Symbol(C, Decl(varianceMeasurement.ts, 50, 7))
}
declare const fn: Fn<string, number>;
>fn : Symbol(fn, Decl(varianceMeasurement.ts, 53, 13))
>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31))
// Contravariant in A
const fn1: Fn<unknown, number> = fn; // Error
>fn1 : Symbol(fn1, Decl(varianceMeasurement.ts, 56, 5))
>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31))
>fn : Symbol(fn, Decl(varianceMeasurement.ts, 53, 13))
const fn2: Fn<'a', number> = fn;
>fn2 : Symbol(fn2, Decl(varianceMeasurement.ts, 57, 5))
>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31))
>fn : Symbol(fn, Decl(varianceMeasurement.ts, 53, 13))
// Covariant in B
const fn3: Fn<string, unknown> = fn;
>fn3 : Symbol(fn3, Decl(varianceMeasurement.ts, 60, 5))
>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31))
>fn : Symbol(fn, Decl(varianceMeasurement.ts, 53, 13))
const fn4: Fn<string, 0> = fn; // Error
>fn4 : Symbol(fn4, Decl(varianceMeasurement.ts, 61, 5))
>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31))
>fn : Symbol(fn, Decl(varianceMeasurement.ts, 53, 13))

View File

@@ -0,0 +1,133 @@
=== tests/cases/compiler/varianceMeasurement.ts ===
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
interface Foo1<T> {
x: T;
>x : T
y: Foo1<(arg: T) => void>;
>y : Foo1<(arg: T) => void>
>arg : T
}
declare const f10: Foo1<string>;
>f10 : Foo1<string>
const f11: Foo1<'a'> = f10;
>f11 : Foo1<"a">
>f10 : Foo1<string>
const f12: Foo1<unknown> = f10;
>f12 : Foo1<unknown>
>f10 : Foo1<string>
// The type below is invariant in T and is measured as such.
interface Foo2<T> {
x: T;
>x : T
y: { x: (arg: T) => void, y: Foo2<(arg: T) => void>; }
>y : { x: (arg: T) => void; y: Foo2<(arg: T) => void>; }
>x : (arg: T) => void
>arg : T
>y : Foo2<(arg: T) => void>
>arg : T
}
declare const f20: Foo2<string>;
>f20 : Foo2<string>
const f21: Foo2<'a'> = f20;
>f21 : Foo2<"a">
>f20 : Foo2<string>
const f22: Foo2<unknown> = f20;
>f22 : Foo2<unknown>
>f20 : Foo2<string>
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
type Foo3<T> = {
>Foo3 : Foo3<T>
x: T;
>x : T
y: Foo3<(arg: T) => void>;
>y : Foo3<(arg: T) => void>
>arg : T
}
declare const f30: Foo3<string>;
>f30 : Foo3<string>
const f31: Foo3<'a'> = f30;
>f31 : Foo3<"a">
>f30 : Foo3<string>
const f32: Foo3<unknown> = f30;
>f32 : Foo3<unknown>
>f30 : Foo3<string>
// The type below is invariant in T and is measured as such.
type Foo4<T> = {
>Foo4 : Foo4<T>
x: T;
>x : T
y: { x: (arg: T) => void, y: Foo4<(arg: T) => void>; }
>y : { x: (arg: T) => void; y: Foo4<(arg: T) => void>; }
>x : (arg: T) => void
>arg : T
>y : Foo4<(arg: T) => void>
>arg : T
}
declare const f40: Foo4<string>;
>f40 : Foo4<string>
const f41: Foo4<'a'> = f40;
>f41 : Foo4<"a">
>f40 : Foo4<string>
const f42: Foo4<unknown> = f40;
>f42 : Foo4<unknown>
>f40 : Foo4<string>
// Repro from #3580
interface Fn<A, B> {
(a: A): B;
>a : A
then<C>(next: Fn<B, C>): Fn<A, C>;
>then : <C>(next: Fn<B, C>) => Fn<A, C>
>next : Fn<B, C>
}
declare const fn: Fn<string, number>;
>fn : Fn<string, number>
// Contravariant in A
const fn1: Fn<unknown, number> = fn; // Error
>fn1 : Fn<unknown, number>
>fn : Fn<string, number>
const fn2: Fn<'a', number> = fn;
>fn2 : Fn<"a", number>
>fn : Fn<string, number>
// Covariant in B
const fn3: Fn<string, unknown> = fn;
>fn3 : Fn<string, unknown>
>fn : Fn<string, number>
const fn4: Fn<string, 0> = fn; // Error
>fn4 : Fn<string, 0>
>fn : Fn<string, number>

View File

@@ -0,0 +1,64 @@
// @strict: true
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
interface Foo1<T> {
x: T;
y: Foo1<(arg: T) => void>;
}
declare const f10: Foo1<string>;
const f11: Foo1<'a'> = f10;
const f12: Foo1<unknown> = f10;
// The type below is invariant in T and is measured as such.
interface Foo2<T> {
x: T;
y: { x: (arg: T) => void, y: Foo2<(arg: T) => void>; }
}
declare const f20: Foo2<string>;
const f21: Foo2<'a'> = f20;
const f22: Foo2<unknown> = f20;
// The type below should be invariant in T but is measured as covariant because
// we don't analyze recursive references.
type Foo3<T> = {
x: T;
y: Foo3<(arg: T) => void>;
}
declare const f30: Foo3<string>;
const f31: Foo3<'a'> = f30;
const f32: Foo3<unknown> = f30;
// The type below is invariant in T and is measured as such.
type Foo4<T> = {
x: T;
y: { x: (arg: T) => void, y: Foo4<(arg: T) => void>; }
}
declare const f40: Foo4<string>;
const f41: Foo4<'a'> = f40;
const f42: Foo4<unknown> = f40;
// Repro from #3580
interface Fn<A, B> {
(a: A): B;
then<C>(next: Fn<B, C>): Fn<A, C>;
}
declare const fn: Fn<string, number>;
// Contravariant in A
const fn1: Fn<unknown, number> = fn; // Error
const fn2: Fn<'a', number> = fn;
// Covariant in B
const fn3: Fn<string, unknown> = fn;
const fn4: Fn<string, 0> = fn; // Error