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
48 changes: 27 additions & 21 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23190,29 +23190,35 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// the `-?` modifier in a mapped type (where, no matter how the inputs are related, the outputs still might not be)
related = relation === identityRelation ? isRelatedTo(s, t, RecursionFlags.Both, /*reportErrors*/ false) : compareTypesIdentical(s, t);
}
else if (variance === VarianceFlags.Covariant) {
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
else if (variance === VarianceFlags.Contravariant) {
related = isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
else if (variance === VarianceFlags.Bivariant) {
// In the bivariant case we first compare contravariantly without reporting
// errors. Then, if that doesn't succeed, we compare covariantly with error
// reporting. Thus, error elaboration will be based on the the covariant check,
// which is generally easier to reason about.
related = isRelatedTo(t, s, RecursionFlags.Both, /*reportErrors*/ false);
if (!related) {
else {
// Propagate unreliable variance flag
if (inVarianceComputation && varianceFlags & VarianceFlags.Unreliable) {
instantiateType(s, reportUnreliableMapper);
}
if (variance === VarianceFlags.Covariant) {
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
}
else {
// In the invariant case we first compare covariantly, and only when that
// succeeds do we proceed to compare contravariantly. Thus, error elaboration
// will typically be based on the covariant check.
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
if (related) {
related &= isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
else if (variance === VarianceFlags.Contravariant) {
related = isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
else if (variance === VarianceFlags.Bivariant) {
// In the bivariant case we first compare contravariantly without reporting
// errors. Then, if that doesn't succeed, we compare covariantly with error
// reporting. Thus, error elaboration will be based on the the covariant check,
// which is generally easier to reason about.
related = isRelatedTo(t, s, RecursionFlags.Both, /*reportErrors*/ false);
if (!related) {
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
}
else {
// In the invariant case we first compare covariantly, and only when that
// succeeds do we proceed to compare contravariantly. Thus, error elaboration
// will typically be based on the covariant check.
related = isRelatedTo(s, t, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
if (related) {
related &= isRelatedTo(t, s, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
}
}
}
if (!related) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//// [tests/cases/compiler/circularlySimplifyingConditionalTypesNoCrash.ts] ////

=== Performance Stats ===
Instantiation count: 1,000

=== circularlySimplifyingConditionalTypesNoCrash.ts ===
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
>Omit : Omit<T, K>
Expand Down
50 changes: 50 additions & 0 deletions tests/baselines/reference/variancePropagation.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//// [tests/cases/compiler/variancePropagation.ts] ////

=== variancePropagation.ts ===
// https://github.com/microsoft/TypeScript/issues/62606

interface DerivedTable<S extends { base: any; new: any }> {
>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0))
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
>base : Symbol(base, Decl(variancePropagation.ts, 2, 34))
>new : Symbol(new, Decl(variancePropagation.ts, 2, 45))

// Error disappears when these property declarations are reversed
schema: S["base"] & S["new"]
>schema : Symbol(DerivedTable.schema, Decl(variancePropagation.ts, 2, 59))
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))

readonlySchema: Readonly<S["base"] & S["new"]>
>readonlySchema : Symbol(DerivedTable.readonlySchema, Decl(variancePropagation.ts, 4, 32))
>Readonly : Symbol(Readonly, Decl(lib.es5.d.ts, --, --))
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
>S : Symbol(S, Decl(variancePropagation.ts, 2, 23))
}

interface Base { baseProp: number; }
>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1))
>baseProp : Symbol(Base.baseProp, Decl(variancePropagation.ts, 8, 16))

interface New { newProp: boolean; }
>New : Symbol(New, Decl(variancePropagation.ts, 8, 36))
>newProp : Symbol(New.newProp, Decl(variancePropagation.ts, 9, 16))

declare const source: DerivedTable<{ base: Base, new: New }>
>source : Symbol(source, Decl(variancePropagation.ts, 11, 13))
>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0))
>base : Symbol(base, Decl(variancePropagation.ts, 11, 36))
>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1))
>new : Symbol(new, Decl(variancePropagation.ts, 11, 48))
>New : Symbol(New, Decl(variancePropagation.ts, 8, 36))

const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error
>destination : Symbol(destination, Decl(variancePropagation.ts, 12, 5))
>DerivedTable : Symbol(DerivedTable, Decl(variancePropagation.ts, 0, 0))
>base : Symbol(base, Decl(variancePropagation.ts, 12, 33))
>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1))
>new : Symbol(new, Decl(variancePropagation.ts, 12, 45))
>New : Symbol(New, Decl(variancePropagation.ts, 8, 36))
>Base : Symbol(Base, Decl(variancePropagation.ts, 6, 1))
>source : Symbol(source, Decl(variancePropagation.ts, 11, 13))

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

=== variancePropagation.ts ===
// https://github.com/microsoft/TypeScript/issues/62606

interface DerivedTable<S extends { base: any; new: any }> {
>base : any
>new : any

// Error disappears when these property declarations are reversed
schema: S["base"] & S["new"]
>schema : S["base"] & S["new"]
> : ^^^^^^^^^^^^^^^^^^^^

readonlySchema: Readonly<S["base"] & S["new"]>
>readonlySchema : Readonly<S["base"] & S["new"]>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}

interface Base { baseProp: number; }
>baseProp : number
> : ^^^^^^

interface New { newProp: boolean; }
>newProp : boolean
> : ^^^^^^^

declare const source: DerivedTable<{ base: Base, new: New }>
>source : DerivedTable<{ base: Base; new: New; }>
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^
>base : Base
> : ^^^^
>new : New
> : ^^^

const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error
>destination : DerivedTable<{ base: Base; new: New & Base; }>
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^
>base : Base
> : ^^^^
>new : New & Base
> : ^^^^^^^^^^
>source : DerivedTable<{ base: Base; new: New; }>
> : ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^

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

// https://github.com/microsoft/TypeScript/issues/62606

interface DerivedTable<S extends { base: any; new: any }> {
// Error disappears when these property declarations are reversed
schema: S["base"] & S["new"]
readonlySchema: Readonly<S["base"] & S["new"]>
}

interface Base { baseProp: number; }
interface New { newProp: boolean; }

declare const source: DerivedTable<{ base: Base, new: New }>
const destination: DerivedTable<{ base: Base; new: New & Base }> = source; // Error