From 4879802478382e16ee6109e6fe4e218c49545dd5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 17 Jan 2020 09:10:00 -0800 Subject: [PATCH 1/3] No covariance default for recursive references in variance measurement --- src/compiler/checker.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 966e6f4e4c75c..e5f2932aab7f3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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); @@ -15403,6 +15404,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; @@ -15599,6 +15603,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((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(source), getTypeArguments(target), variances, intersectionState); if (varianceResult !== undefined) { return varianceResult; @@ -16418,8 +16428,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(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] { let variances = cache.variances; if (!variances) { @@ -16462,9 +16471,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); } From af7f5b1b48b77793d5368b424d7e78d984ab33b5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 17 Jan 2020 09:18:39 -0800 Subject: [PATCH 2/3] Add tests --- .../reference/varianceMeasurement.errors.txt | 122 ++++++++++++ .../reference/varianceMeasurement.js | 83 ++++++++ .../reference/varianceMeasurement.symbols | 185 ++++++++++++++++++ .../reference/varianceMeasurement.types | 133 +++++++++++++ tests/cases/compiler/varianceMeasurement.ts | 64 ++++++ 5 files changed, 587 insertions(+) create mode 100644 tests/baselines/reference/varianceMeasurement.errors.txt create mode 100644 tests/baselines/reference/varianceMeasurement.js create mode 100644 tests/baselines/reference/varianceMeasurement.symbols create mode 100644 tests/baselines/reference/varianceMeasurement.types create mode 100644 tests/cases/compiler/varianceMeasurement.ts diff --git a/tests/baselines/reference/varianceMeasurement.errors.txt b/tests/baselines/reference/varianceMeasurement.errors.txt new file mode 100644 index 0000000000000..eb77b28564669 --- /dev/null +++ b/tests/baselines/reference/varianceMeasurement.errors.txt @@ -0,0 +1,122 @@ +tests/cases/compiler/varianceMeasurement.ts(10,7): error TS2322: Type 'Foo1' 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' 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' is not assignable to type 'Foo2'. + 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' 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' 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' is not assignable to type 'Foo4'. + 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' is not assignable to type 'Fn'. + Type 'unknown' is not assignable to type 'string'. +tests/cases/compiler/varianceMeasurement.ts(62,7): error TS2322: Type 'Fn' is not assignable to type 'Fn'. + 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 { + x: T; + y: Foo1<(arg: T) => void>; + } + + declare const f10: Foo1; + const f11: Foo1<'a'> = f10; + ~~~ +!!! error TS2322: Type 'Foo1' is not assignable to type 'Foo1<"a">'. +!!! error TS2322: Type 'string' is not assignable to type '"a"'. + const f12: Foo1 = f10; + + // The type below is invariant in T and is measured as such. + + interface Foo2 { + x: T; + y: { x: (arg: T) => void, y: Foo2<(arg: T) => void>; } + } + + declare const f20: Foo2; + const f21: Foo2<'a'> = f20; + ~~~ +!!! error TS2322: Type 'Foo2' 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 = f20; + ~~~ +!!! error TS2322: Type 'Foo2' is not assignable to type 'Foo2'. +!!! 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 = { + x: T; + y: Foo3<(arg: T) => void>; + } + + declare const f30: Foo3; + const f31: Foo3<'a'> = f30; + ~~~ +!!! error TS2322: Type 'Foo3' is not assignable to type 'Foo3<"a">'. +!!! error TS2322: Type 'string' is not assignable to type '"a"'. + const f32: Foo3 = f30; + + // The type below is invariant in T and is measured as such. + + type Foo4 = { + x: T; + y: { x: (arg: T) => void, y: Foo4<(arg: T) => void>; } + } + + declare const f40: Foo4; + const f41: Foo4<'a'> = f40; + ~~~ +!!! error TS2322: Type 'Foo4' 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 = f40; + ~~~ +!!! error TS2322: Type 'Foo4' is not assignable to type 'Foo4'. +!!! 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: A): B; + then(next: Fn): Fn; + } + + declare const fn: Fn; + + // Contravariant in A + const fn1: Fn = fn; // Error + ~~~ +!!! error TS2322: Type 'Fn' is not assignable to type 'Fn'. +!!! error TS2322: Type 'unknown' is not assignable to type 'string'. + const fn2: Fn<'a', number> = fn; + + // Covariant in B + const fn3: Fn = fn; + const fn4: Fn = fn; // Error + ~~~ +!!! error TS2322: Type 'Fn' is not assignable to type 'Fn'. +!!! error TS2322: Type 'number' is not assignable to type '0'. + \ No newline at end of file diff --git a/tests/baselines/reference/varianceMeasurement.js b/tests/baselines/reference/varianceMeasurement.js new file mode 100644 index 0000000000000..b597e5d24639b --- /dev/null +++ b/tests/baselines/reference/varianceMeasurement.js @@ -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 { + x: T; + y: Foo1<(arg: T) => void>; +} + +declare const f10: Foo1; +const f11: Foo1<'a'> = f10; +const f12: Foo1 = f10; + +// The type below is invariant in T and is measured as such. + +interface Foo2 { + x: T; + y: { x: (arg: T) => void, y: Foo2<(arg: T) => void>; } +} + +declare const f20: Foo2; +const f21: Foo2<'a'> = f20; +const f22: Foo2 = f20; + +// The type below should be invariant in T but is measured as covariant because +// we don't analyze recursive references. + +type Foo3 = { + x: T; + y: Foo3<(arg: T) => void>; +} + +declare const f30: Foo3; +const f31: Foo3<'a'> = f30; +const f32: Foo3 = f30; + +// The type below is invariant in T and is measured as such. + +type Foo4 = { + x: T; + y: { x: (arg: T) => void, y: Foo4<(arg: T) => void>; } +} + +declare const f40: Foo4; +const f41: Foo4<'a'> = f40; +const f42: Foo4 = f40; + +// Repro from #3580 + +interface Fn { + (a: A): B; + then(next: Fn): Fn; +} + +declare const fn: Fn; + +// Contravariant in A +const fn1: Fn = fn; // Error +const fn2: Fn<'a', number> = fn; + +// Covariant in B +const fn3: Fn = fn; +const fn4: Fn = 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 diff --git a/tests/baselines/reference/varianceMeasurement.symbols b/tests/baselines/reference/varianceMeasurement.symbols new file mode 100644 index 0000000000000..98d36fe16f91d --- /dev/null +++ b/tests/baselines/reference/varianceMeasurement.symbols @@ -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 { +>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; +>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 = 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 { +>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; +>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 = 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 = { +>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; +>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 = 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 = { +>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; +>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 = 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 { +>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(next: Fn): Fn; +>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; +>fn : Symbol(fn, Decl(varianceMeasurement.ts, 53, 13)) +>Fn : Symbol(Fn, Decl(varianceMeasurement.ts, 44, 31)) + +// Contravariant in A +const fn1: Fn = 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 = 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 = 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)) + diff --git a/tests/baselines/reference/varianceMeasurement.types b/tests/baselines/reference/varianceMeasurement.types new file mode 100644 index 0000000000000..83be34759f1ed --- /dev/null +++ b/tests/baselines/reference/varianceMeasurement.types @@ -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 { + x: T; +>x : T + + y: Foo1<(arg: T) => void>; +>y : Foo1<(arg: T) => void> +>arg : T +} + +declare const f10: Foo1; +>f10 : Foo1 + +const f11: Foo1<'a'> = f10; +>f11 : Foo1<"a"> +>f10 : Foo1 + +const f12: Foo1 = f10; +>f12 : Foo1 +>f10 : Foo1 + +// The type below is invariant in T and is measured as such. + +interface Foo2 { + 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; +>f20 : Foo2 + +const f21: Foo2<'a'> = f20; +>f21 : Foo2<"a"> +>f20 : Foo2 + +const f22: Foo2 = f20; +>f22 : Foo2 +>f20 : Foo2 + +// The type below should be invariant in T but is measured as covariant because +// we don't analyze recursive references. + +type Foo3 = { +>Foo3 : Foo3 + + x: T; +>x : T + + y: Foo3<(arg: T) => void>; +>y : Foo3<(arg: T) => void> +>arg : T +} + +declare const f30: Foo3; +>f30 : Foo3 + +const f31: Foo3<'a'> = f30; +>f31 : Foo3<"a"> +>f30 : Foo3 + +const f32: Foo3 = f30; +>f32 : Foo3 +>f30 : Foo3 + +// The type below is invariant in T and is measured as such. + +type Foo4 = { +>Foo4 : Foo4 + + 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; +>f40 : Foo4 + +const f41: Foo4<'a'> = f40; +>f41 : Foo4<"a"> +>f40 : Foo4 + +const f42: Foo4 = f40; +>f42 : Foo4 +>f40 : Foo4 + +// Repro from #3580 + +interface Fn { + (a: A): B; +>a : A + + then(next: Fn): Fn; +>then : (next: Fn) => Fn +>next : Fn +} + +declare const fn: Fn; +>fn : Fn + +// Contravariant in A +const fn1: Fn = fn; // Error +>fn1 : Fn +>fn : Fn + +const fn2: Fn<'a', number> = fn; +>fn2 : Fn<"a", number> +>fn : Fn + +// Covariant in B +const fn3: Fn = fn; +>fn3 : Fn +>fn : Fn + +const fn4: Fn = fn; // Error +>fn4 : Fn +>fn : Fn + diff --git a/tests/cases/compiler/varianceMeasurement.ts b/tests/cases/compiler/varianceMeasurement.ts new file mode 100644 index 0000000000000..6c327291d97a2 --- /dev/null +++ b/tests/cases/compiler/varianceMeasurement.ts @@ -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 { + x: T; + y: Foo1<(arg: T) => void>; +} + +declare const f10: Foo1; +const f11: Foo1<'a'> = f10; +const f12: Foo1 = f10; + +// The type below is invariant in T and is measured as such. + +interface Foo2 { + x: T; + y: { x: (arg: T) => void, y: Foo2<(arg: T) => void>; } +} + +declare const f20: Foo2; +const f21: Foo2<'a'> = f20; +const f22: Foo2 = f20; + +// The type below should be invariant in T but is measured as covariant because +// we don't analyze recursive references. + +type Foo3 = { + x: T; + y: Foo3<(arg: T) => void>; +} + +declare const f30: Foo3; +const f31: Foo3<'a'> = f30; +const f32: Foo3 = f30; + +// The type below is invariant in T and is measured as such. + +type Foo4 = { + x: T; + y: { x: (arg: T) => void, y: Foo4<(arg: T) => void>; } +} + +declare const f40: Foo4; +const f41: Foo4<'a'> = f40; +const f42: Foo4 = f40; + +// Repro from #3580 + +interface Fn { + (a: A): B; + then(next: Fn): Fn; +} + +declare const fn: Fn; + +// Contravariant in A +const fn1: Fn = fn; // Error +const fn2: Fn<'a', number> = fn; + +// Covariant in B +const fn3: Fn = fn; +const fn4: Fn = fn; // Error From b4536062ec1f5011242ef0db4a3f4cb35e8e6853 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 17 Jan 2020 09:27:13 -0800 Subject: [PATCH 3/3] Accept new baselines --- ...eckInfiniteExpansionTermination.errors.txt | 32 ------------------- .../recursiveTypeComparison.errors.txt | 27 ---------------- 2 files changed, 59 deletions(-) delete mode 100644 tests/baselines/reference/checkInfiniteExpansionTermination.errors.txt delete mode 100644 tests/baselines/reference/recursiveTypeComparison.errors.txt diff --git a/tests/baselines/reference/checkInfiniteExpansionTermination.errors.txt b/tests/baselines/reference/checkInfiniteExpansionTermination.errors.txt deleted file mode 100644 index 014087ff6275e..0000000000000 --- a/tests/baselines/reference/checkInfiniteExpansionTermination.errors.txt +++ /dev/null @@ -1,32 +0,0 @@ -tests/cases/compiler/checkInfiniteExpansionTermination.ts(16,1): error TS2322: Type 'ISubject' is not assignable to type 'IObservable'. - Types of property 'n' are incompatible. - Type 'IObservable' is not assignable to type 'IObservable'. - 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 { - n: IObservable; // Needed, must be T[] - } - - // Needed - interface ISubject extends IObservable { } - - interface Foo { x } - interface Bar { y } - - var values: IObservable; - var values2: ISubject; - values = values2; - ~~~~~~ -!!! error TS2322: Type 'ISubject' is not assignable to type 'IObservable'. -!!! error TS2322: Types of property 'n' are incompatible. -!!! error TS2322: Type 'IObservable' is not assignable to type 'IObservable'. -!!! 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. - \ No newline at end of file diff --git a/tests/baselines/reference/recursiveTypeComparison.errors.txt b/tests/baselines/reference/recursiveTypeComparison.errors.txt deleted file mode 100644 index f647763b2e275..0000000000000 --- a/tests/baselines/reference/recursiveTypeComparison.errors.txt +++ /dev/null @@ -1,27 +0,0 @@ -tests/cases/compiler/recursiveTypeComparison.ts(14,5): error TS2322: Type 'Observable<{}>' is not assignable to type 'Property'. - Types of property 'needThisOne' are incompatible. - Type 'Observable<{}>' is not assignable to type 'Observable'. - 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 { - // This member can't be of type T, Property, or Observable - needThisOne: Observable; - // Add more to make it slower - expo1: Property; // 0.31 seconds in check - expo2: Property; // 3.11 seconds - expo3: Property; // 82.28 seconds - } - interface Property extends Observable { } - - var p: Observable<{}>; - var stuck: Property = p; - ~~~~~ -!!! error TS2322: Type 'Observable<{}>' is not assignable to type 'Property'. -!!! error TS2322: Types of property 'needThisOne' are incompatible. -!!! error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable'. -!!! error TS2322: Type '{}' is not assignable to type 'number'. - \ No newline at end of file