mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-12 01:48:33 -05:00
Improve variance measurement (#36261)
* No covariance default for recursive references in variance measurement * Add tests * Accept new baselines
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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'.
|
||||
|
||||
122
tests/baselines/reference/varianceMeasurement.errors.txt
Normal file
122
tests/baselines/reference/varianceMeasurement.errors.txt
Normal 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'.
|
||||
|
||||
83
tests/baselines/reference/varianceMeasurement.js
Normal file
83
tests/baselines/reference/varianceMeasurement.js
Normal 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
|
||||
185
tests/baselines/reference/varianceMeasurement.symbols
Normal file
185
tests/baselines/reference/varianceMeasurement.symbols
Normal 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))
|
||||
|
||||
133
tests/baselines/reference/varianceMeasurement.types
Normal file
133
tests/baselines/reference/varianceMeasurement.types
Normal 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>
|
||||
|
||||
64
tests/cases/compiler/varianceMeasurement.ts
Normal file
64
tests/cases/compiler/varianceMeasurement.ts
Normal 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
|
||||
Reference in New Issue
Block a user