Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tsc/internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ type Checker struct {
uniqueLiteralType *Type
uniqueLiteralMapper *TypeMapper
reliabilityFlags RelationComparisonResult
sawCircularVariance bool
reportUnreliableMapper *TypeMapper
reportUnmeasurableMapper *TypeMapper
restrictiveMapper *TypeMapper
Expand Down
15 changes: 13 additions & 2 deletions tsc/internal/checker/relater.go
Original file line number Diff line number Diff line change
Expand Up @@ -1369,7 +1369,9 @@ func (c *Checker) getVariancesWorker(symbol *ast.Symbol, typeParameters []*Type)
variance = VarianceFlagsContravariant
default:
saveReliabilityFlags := c.reliabilityFlags
saveSawCircularVariance := c.sawCircularVariance
c.reliabilityFlags = 0
c.sawCircularVariance = false
// We first compare instantiations where the type parameter is replaced with
// marker types that have a known subtype relationship. From this we can infer
// invariance, covariance, contravariance or bivariance.
Expand All @@ -1380,9 +1382,15 @@ func (c *Checker) getVariancesWorker(symbol *ast.Symbol, typeParameters []*Type)
// If the instantiations appear to be related bivariantly it may be because the
// type parameter is independent (i.e. it isn't witnessed anywhere in the generic
// type). To determine this we compare instantiations where the type parameter is
// replaced with marker types that are known to be unrelated.
// replaced with marker types that are known to be unrelated. However, if the comparisons
// relied on variances of circular types still being computed, the type parameter may be
// witnessed through those types, so we can only conclude that the variance is unmeasurable.
if variance == VarianceFlagsBivariant && c.isTypeAssignableTo(c.createMarkerType(symbol, tp, c.markerOtherType), typeWithSuper) {
variance = VarianceFlagsIndependent
if c.sawCircularVariance {
variance |= VarianceFlagsUnmeasurable
} else {
variance = VarianceFlagsIndependent
}
}
if c.reliabilityFlags&RelationComparisonResultReportsUnmeasurable != 0 {
variance |= VarianceFlagsUnmeasurable
Expand All @@ -1391,6 +1399,7 @@ func (c *Checker) getVariancesWorker(symbol *ast.Symbol, typeParameters []*Type)
variance |= VarianceFlagsUnreliable
}
c.reliabilityFlags = saveReliabilityFlags
c.sawCircularVariance = saveSawCircularVariance
}
// If variance computation was restarted due to a circularity we may have already
// computed variances for this generic type. If so, we exit early.
Expand Down Expand Up @@ -3426,6 +3435,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo
target.alias != nil && source.alias.symbol == target.alias.symbol && !(r.c.isMarkerType(source) || r.c.isMarkerType(target)) {
variances := r.c.getAliasVariances(source.alias.symbol)
if len(variances) == 0 {
r.c.sawCircularVariance = true
return TernaryUnknown
}
params := r.c.typeAliasLinks.Get(source.alias.symbol).typeParameters
Expand Down Expand Up @@ -3865,6 +3875,7 @@ func (r *Relater) structuredTypeRelatedToWorker(source *Type, target *Type, repo
// effectively means we measure variance only from type parameter occurrences that aren't nested in
// recursive instantiations of the generic type.
if len(variances) == 0 {
r.c.sawCircularVariance = true
return TernaryUnknown
}
varianceResult, ok := relateVariances(r.c.getTypeArguments(source), r.c.getTypeArguments(target), variances, intersectionState)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
circularVariance3.ts(6,14): error TS2322: Type 'Handler<string, string>' is not assignable to type 'Handler<number, string>'.
Types of parameters 'params' and 'params' are incompatible.
Type 'Params<number, string>' is not assignable to type 'Params<string, string>'.
Type 'string' is not assignable to type 'number'.
circularVariance3.ts(7,14): error TS2322: Type 'Handler<string, string>' is not assignable to type 'Handler<boolean, { x: 1; }>'.
Types of parameters 'params' and 'params' are incompatible.
Type 'Params<boolean, { x: 1; }>' is not assignable to type 'Params<string, string>'.
Type 'string' is not assignable to type 'boolean'.


==== circularVariance3.ts (2 errors) ====
type Params<T, K> = { config: Config<T, K> };
type Handler<T, K> = (params: Params<T, K>) => void;
type Config<T, K> = { read: (item: T) => void; handler: Handler<T, K> };

declare const a: Handler<string, string>;
export const b: Handler<number, string> = a; // Error
~
!!! error TS2322: Type 'Handler<string, string>' is not assignable to type 'Handler<number, string>'.
!!! error TS2322: Types of parameters 'params' and 'params' are incompatible.
!!! error TS2322: Type 'Params<number, string>' is not assignable to type 'Params<string, string>'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
export const c: Handler<boolean, { x: 1 }> = a; // Error
~
!!! error TS2322: Type 'Handler<string, string>' is not assignable to type 'Handler<boolean, { x: 1; }>'.
!!! error TS2322: Types of parameters 'params' and 'params' are incompatible.
!!! error TS2322: Type 'Params<boolean, { x: 1; }>' is not assignable to type 'Params<string, string>'.
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
export const d: Handler<string, number> = a;

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//// [tests/cases/compiler/circularVariance3.ts] ////

=== circularVariance3.ts ===
type Params<T, K> = { config: Config<T, K> };
>Params : Symbol(Params, Decl(circularVariance3.ts, 0, 0))
>T : Symbol(T, Decl(circularVariance3.ts, 0, 12))
>K : Symbol(K, Decl(circularVariance3.ts, 0, 14))
>config : Symbol(config, Decl(circularVariance3.ts, 0, 21))
>Config : Symbol(Config, Decl(circularVariance3.ts, 1, 52))
>T : Symbol(T, Decl(circularVariance3.ts, 0, 12))
>K : Symbol(K, Decl(circularVariance3.ts, 0, 14))

type Handler<T, K> = (params: Params<T, K>) => void;
>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45))
>T : Symbol(T, Decl(circularVariance3.ts, 1, 13))
>K : Symbol(K, Decl(circularVariance3.ts, 1, 15))
>params : Symbol(params, Decl(circularVariance3.ts, 1, 22))
>Params : Symbol(Params, Decl(circularVariance3.ts, 0, 0))
>T : Symbol(T, Decl(circularVariance3.ts, 1, 13))
>K : Symbol(K, Decl(circularVariance3.ts, 1, 15))

type Config<T, K> = { read: (item: T) => void; handler: Handler<T, K> };
>Config : Symbol(Config, Decl(circularVariance3.ts, 1, 52))
>T : Symbol(T, Decl(circularVariance3.ts, 2, 12))
>K : Symbol(K, Decl(circularVariance3.ts, 2, 14))
>read : Symbol(read, Decl(circularVariance3.ts, 2, 21))
>item : Symbol(item, Decl(circularVariance3.ts, 2, 29))
>T : Symbol(T, Decl(circularVariance3.ts, 2, 12))
>handler : Symbol(handler, Decl(circularVariance3.ts, 2, 46))
>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45))
>T : Symbol(T, Decl(circularVariance3.ts, 2, 12))
>K : Symbol(K, Decl(circularVariance3.ts, 2, 14))

declare const a: Handler<string, string>;
>a : Symbol(a, Decl(circularVariance3.ts, 4, 13))
>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45))

export const b: Handler<number, string> = a; // Error
>b : Symbol(b, Decl(circularVariance3.ts, 5, 12))
>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45))
>a : Symbol(a, Decl(circularVariance3.ts, 4, 13))

export const c: Handler<boolean, { x: 1 }> = a; // Error
>c : Symbol(c, Decl(circularVariance3.ts, 6, 12))
>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45))
>x : Symbol(x, Decl(circularVariance3.ts, 6, 34))
>a : Symbol(a, Decl(circularVariance3.ts, 4, 13))

export const d: Handler<string, number> = a;
>d : Symbol(d, Decl(circularVariance3.ts, 7, 12))
>Handler : Symbol(Handler, Decl(circularVariance3.ts, 0, 45))
>a : Symbol(a, Decl(circularVariance3.ts, 4, 13))

33 changes: 33 additions & 0 deletions tsc/testdata/baselines/reference/compiler/circularVariance3.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//// [tests/cases/compiler/circularVariance3.ts] ////

=== circularVariance3.ts ===
type Params<T, K> = { config: Config<T, K> };
>Params : Params<T, K>
>config : Config<T, K>

type Handler<T, K> = (params: Params<T, K>) => void;
>Handler : Handler<T, K>
>params : Params<T, K>

type Config<T, K> = { read: (item: T) => void; handler: Handler<T, K> };
>Config : Config<T, K>
>read : (item: T) => void
>item : T
>handler : Handler<T, K>

declare const a: Handler<string, string>;
>a : Handler<string, string>

export const b: Handler<number, string> = a; // Error
>b : Handler<number, string>
>a : Handler<string, string>

export const c: Handler<boolean, { x: 1 }> = a; // Error
>c : Handler<boolean, { x: 1; }>
>x : 1
>a : Handler<string, string>

export const d: Handler<string, number> = a;
>d : Handler<string, number>
>a : Handler<string, string>

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
circularVariance4.ts(6,14): error TS2322: Type 'Params<string, string>' is not assignable to type 'Params<number, string>'.
Type 'number' is not assignable to type 'string'.
circularVariance4.ts(9,14): error TS2322: Type 'Handler<string, string>' is not assignable to type 'Handler<number, string>'.
Types of parameters 'params' and 'params' are incompatible.
Type 'Params<number, string>' is not assignable to type 'Params<string, string>'.
Type 'string' is not assignable to type 'number'.
circularVariance4.ts(10,14): error TS2322: Type 'Handler<string, string>' is not assignable to type 'Handler<boolean, { x: 1; }>'.
Types of parameters 'params' and 'params' are incompatible.
Type 'Params<boolean, { x: 1; }>' is not assignable to type 'Params<string, string>'.
Type 'string' is not assignable to type 'boolean'.


==== circularVariance4.ts (3 errors) ====
type Params<T, K> = { config: Config<T, K> };
type Handler<T, K> = (params: Params<T, K>) => void;
type Config<T, K> = { read: (item: T) => void; handler: Handler<T, K> };

declare const p: Params<string, string>;
export const q: Params<number, string> = p; // Error
~
!!! error TS2322: Type 'Params<string, string>' is not assignable to type 'Params<number, string>'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.

declare const a: Handler<string, string>;
export const b: Handler<number, string> = a; // Error
~
!!! error TS2322: Type 'Handler<string, string>' is not assignable to type 'Handler<number, string>'.
!!! error TS2322: Types of parameters 'params' and 'params' are incompatible.
!!! error TS2322: Type 'Params<number, string>' is not assignable to type 'Params<string, string>'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
export const c: Handler<boolean, { x: 1 }> = a; // Error
~
!!! error TS2322: Type 'Handler<string, string>' is not assignable to type 'Handler<boolean, { x: 1; }>'.
!!! error TS2322: Types of parameters 'params' and 'params' are incompatible.
!!! error TS2322: Type 'Params<boolean, { x: 1; }>' is not assignable to type 'Params<string, string>'.
!!! error TS2322: Type 'string' is not assignable to type 'boolean'.
export const d: Handler<string, number> = a;

Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//// [tests/cases/compiler/circularVariance4.ts] ////

=== circularVariance4.ts ===
type Params<T, K> = { config: Config<T, K> };
>Params : Symbol(Params, Decl(circularVariance4.ts, 0, 0))
>T : Symbol(T, Decl(circularVariance4.ts, 0, 12))
>K : Symbol(K, Decl(circularVariance4.ts, 0, 14))
>config : Symbol(config, Decl(circularVariance4.ts, 0, 21))
>Config : Symbol(Config, Decl(circularVariance4.ts, 1, 52))
>T : Symbol(T, Decl(circularVariance4.ts, 0, 12))
>K : Symbol(K, Decl(circularVariance4.ts, 0, 14))

type Handler<T, K> = (params: Params<T, K>) => void;
>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45))
>T : Symbol(T, Decl(circularVariance4.ts, 1, 13))
>K : Symbol(K, Decl(circularVariance4.ts, 1, 15))
>params : Symbol(params, Decl(circularVariance4.ts, 1, 22))
>Params : Symbol(Params, Decl(circularVariance4.ts, 0, 0))
>T : Symbol(T, Decl(circularVariance4.ts, 1, 13))
>K : Symbol(K, Decl(circularVariance4.ts, 1, 15))

type Config<T, K> = { read: (item: T) => void; handler: Handler<T, K> };
>Config : Symbol(Config, Decl(circularVariance4.ts, 1, 52))
>T : Symbol(T, Decl(circularVariance4.ts, 2, 12))
>K : Symbol(K, Decl(circularVariance4.ts, 2, 14))
>read : Symbol(read, Decl(circularVariance4.ts, 2, 21))
>item : Symbol(item, Decl(circularVariance4.ts, 2, 29))
>T : Symbol(T, Decl(circularVariance4.ts, 2, 12))
>handler : Symbol(handler, Decl(circularVariance4.ts, 2, 46))
>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45))
>T : Symbol(T, Decl(circularVariance4.ts, 2, 12))
>K : Symbol(K, Decl(circularVariance4.ts, 2, 14))

declare const p: Params<string, string>;
>p : Symbol(p, Decl(circularVariance4.ts, 4, 13))
>Params : Symbol(Params, Decl(circularVariance4.ts, 0, 0))

export const q: Params<number, string> = p; // Error
>q : Symbol(q, Decl(circularVariance4.ts, 5, 12))
>Params : Symbol(Params, Decl(circularVariance4.ts, 0, 0))
>p : Symbol(p, Decl(circularVariance4.ts, 4, 13))

declare const a: Handler<string, string>;
>a : Symbol(a, Decl(circularVariance4.ts, 7, 13))
>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45))

export const b: Handler<number, string> = a; // Error
>b : Symbol(b, Decl(circularVariance4.ts, 8, 12))
>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45))
>a : Symbol(a, Decl(circularVariance4.ts, 7, 13))

export const c: Handler<boolean, { x: 1 }> = a; // Error
>c : Symbol(c, Decl(circularVariance4.ts, 9, 12))
>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45))
>x : Symbol(x, Decl(circularVariance4.ts, 9, 34))
>a : Symbol(a, Decl(circularVariance4.ts, 7, 13))

export const d: Handler<string, number> = a;
>d : Symbol(d, Decl(circularVariance4.ts, 10, 12))
>Handler : Symbol(Handler, Decl(circularVariance4.ts, 0, 45))
>a : Symbol(a, Decl(circularVariance4.ts, 7, 13))

40 changes: 40 additions & 0 deletions tsc/testdata/baselines/reference/compiler/circularVariance4.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//// [tests/cases/compiler/circularVariance4.ts] ////

=== circularVariance4.ts ===
type Params<T, K> = { config: Config<T, K> };
>Params : Params<T, K>
>config : Config<T, K>

type Handler<T, K> = (params: Params<T, K>) => void;
>Handler : Handler<T, K>
>params : Params<T, K>

type Config<T, K> = { read: (item: T) => void; handler: Handler<T, K> };
>Config : Config<T, K>
>read : (item: T) => void
>item : T
>handler : Handler<T, K>

declare const p: Params<string, string>;
>p : Params<string, string>

export const q: Params<number, string> = p; // Error
>q : Params<number, string>
>p : Params<string, string>

declare const a: Handler<string, string>;
>a : Handler<string, string>

export const b: Handler<number, string> = a; // Error
>b : Handler<number, string>
>a : Handler<string, string>

export const c: Handler<boolean, { x: 1 }> = a; // Error
>c : Handler<boolean, { x: 1; }>
>x : 1
>a : Handler<string, string>

export const d: Handler<string, number> = a;
>d : Handler<string, number>
>a : Handler<string, string>

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ varianceMeasurement.ts(57,7): error TS2322: Type 'Fn<string, number>' is not ass
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'.
varianceMeasurement.ts(75,7): error TS2322: Type 'C<unknown, number>' is not assignable to type 'C<unknown, string>'.
Type 'number' is not assignable to type 'string'.
Types of property 'sub' are incompatible.
Type 'I<unknown, number>' is not assignable to type 'I<unknown, string>'.
Type 'number' is not assignable to type 'string'.


==== varianceMeasurement.ts (9 errors) ====
Expand Down Expand Up @@ -136,5 +138,7 @@ varianceMeasurement.ts(75,7): error TS2322: Type 'C<unknown, number>' is not ass
const c1: C<unknown, string> = new C<unknown, number>(); // Error
~~
!!! error TS2322: Type 'C<unknown, number>' is not assignable to type 'C<unknown, string>'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
!!! error TS2322: Types of property 'sub' are incompatible.
!!! error TS2322: Type 'I<unknown, number>' is not assignable to type 'I<unknown, string>'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.

11 changes: 11 additions & 0 deletions tsc/testdata/tests/cases/compiler/circularVariance3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// @strict: true
// @noEmit: true

type Params<T, K> = { config: Config<T, K> };
type Handler<T, K> = (params: Params<T, K>) => void;
type Config<T, K> = { read: (item: T) => void; handler: Handler<T, K> };

declare const a: Handler<string, string>;
export const b: Handler<number, string> = a; // Error
export const c: Handler<boolean, { x: 1 }> = a; // Error
export const d: Handler<string, number> = a;
14 changes: 14 additions & 0 deletions tsc/testdata/tests/cases/compiler/circularVariance4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @strict: true
// @noEmit: true

type Params<T, K> = { config: Config<T, K> };
type Handler<T, K> = (params: Params<T, K>) => void;
type Config<T, K> = { read: (item: T) => void; handler: Handler<T, K> };

declare const p: Params<string, string>;
export const q: Params<number, string> = p; // Error

declare const a: Handler<string, string>;
export const b: Handler<number, string> = a; // Error
export const c: Handler<boolean, { x: 1 }> = a; // Error
export const d: Handler<string, number> = a;