Skip to content
Draft
10 changes: 8 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22810,6 +22810,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
tracing?.pop();
}

// if (entry !== undefined) {
// // If the previous entry and the result disagree, then something has gone wrong.
// const entrySucceeded = !!(entry & RelationComparisonResult.Succeeded);
// const resultSucceeded = result !== Ternary.False;
// Debug.assertEqual(entrySucceeded, resultSucceeded, "Cached relationship does not match recalculated result");
// }

if (outofbandVarianceMarkerHandler) {
outofbandVarianceMarkerHandler = originalHandler;
}
Expand Down Expand Up @@ -23522,9 +23529,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
// types are invariant, if any of the type parameters are invariant we reset the reported
// errors and instead force a structural comparison (which will include elaborations that
// reveal the reason).
// We can switch on `reportErrors` here, since varianceCheckFailed guarantees we return `False`,
// we can return `False` early here to skip calculating the structural error message we don't need.
if (varianceCheckFailed && !(reportErrors && some(variances, v => (v & VarianceFlags.VarianceMask) === VarianceFlags.Invariant))) {
if (varianceCheckFailed && !some(variances, v => (v & VarianceFlags.VarianceMask) === VarianceFlags.Invariant)) {
return Ternary.False;
}
// We remember the original error information so we can restore it in case the structural
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//// [tests/cases/compiler/conditionalTypeDoesntSpinForever.ts] ////

=== Performance Stats ===
Type Count: 1,000
Instantiation count: 2,500 -> 5,000
Assignability cache: 1,000
Type Count: 2,500
Instantiation count: 5,000

=== conditionalTypeDoesntSpinForever.ts ===
// A *self-contained* demonstration of the problem follows...
Expand Down
45 changes: 45 additions & 0 deletions tests/baselines/reference/issue55217.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//// [tests/cases/compiler/issue55217.ts] ////

//// [types.ts]
export type ProductName = 'a' | 'b'

export type SubproductNameForProductName<P extends ProductName> = P extends unknown
? keyof EntitiesByProductName[P]
: never

type EntitiesByProductName = {
a: { a1: { value: 'a-a1' } }
b: { b1: { value: 'b-b1' } }
}

export type DiscriminatedUnion<
P extends ProductName = ProductName,
E extends SubproductNameForProductName<P> = SubproductNameForProductName<P>,
> = P extends ProductName
? E extends SubproductNameForProductName<P>
// ? E extends unknown // With unknown, the exception doesn't happen.
? EntitiesByProductName[P][E]
: never
: never

//// [app.ts]
import { SubproductNameForProductName, DiscriminatedUnion, ProductName } from './types'

export const bug = <P extends ProductName>() => {
const subproducts: DiscriminatedUnion<P, SubproductNameForProductName<P>>[] = []
subproducts.map((_: DiscriminatedUnion) => null)
}


//// [types.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//// [app.js]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bug = void 0;
var bug = function () {
var subproducts = [];
subproducts.map(function (_) { return null; });
};
exports.bug = bug;
91 changes: 91 additions & 0 deletions tests/baselines/reference/issue55217.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//// [tests/cases/compiler/issue55217.ts] ////

=== types.ts ===
export type ProductName = 'a' | 'b'
>ProductName : Symbol(ProductName, Decl(types.ts, 0, 0))

export type SubproductNameForProductName<P extends ProductName> = P extends unknown
>SubproductNameForProductName : Symbol(SubproductNameForProductName, Decl(types.ts, 0, 35))
>P : Symbol(P, Decl(types.ts, 2, 41))
>ProductName : Symbol(ProductName, Decl(types.ts, 0, 0))
>P : Symbol(P, Decl(types.ts, 2, 41))

? keyof EntitiesByProductName[P]
>EntitiesByProductName : Symbol(EntitiesByProductName, Decl(types.ts, 4, 9))
>P : Symbol(P, Decl(types.ts, 2, 41))

: never

type EntitiesByProductName = {
>EntitiesByProductName : Symbol(EntitiesByProductName, Decl(types.ts, 4, 9))

a: { a1: { value: 'a-a1' } }
>a : Symbol(a, Decl(types.ts, 6, 30))
>a1 : Symbol(a1, Decl(types.ts, 7, 6))
>value : Symbol(value, Decl(types.ts, 7, 12))

b: { b1: { value: 'b-b1' } }
>b : Symbol(b, Decl(types.ts, 7, 30))
>b1 : Symbol(b1, Decl(types.ts, 8, 6))
>value : Symbol(value, Decl(types.ts, 8, 12))
}

export type DiscriminatedUnion<
>DiscriminatedUnion : Symbol(DiscriminatedUnion, Decl(types.ts, 9, 1))

P extends ProductName = ProductName,
>P : Symbol(P, Decl(types.ts, 11, 31))
>ProductName : Symbol(ProductName, Decl(types.ts, 0, 0))
>ProductName : Symbol(ProductName, Decl(types.ts, 0, 0))

E extends SubproductNameForProductName<P> = SubproductNameForProductName<P>,
>E : Symbol(E, Decl(types.ts, 12, 38))
>SubproductNameForProductName : Symbol(SubproductNameForProductName, Decl(types.ts, 0, 35))
>P : Symbol(P, Decl(types.ts, 11, 31))
>SubproductNameForProductName : Symbol(SubproductNameForProductName, Decl(types.ts, 0, 35))
>P : Symbol(P, Decl(types.ts, 11, 31))

> = P extends ProductName
>P : Symbol(P, Decl(types.ts, 11, 31))
>ProductName : Symbol(ProductName, Decl(types.ts, 0, 0))

? E extends SubproductNameForProductName<P>
>E : Symbol(E, Decl(types.ts, 12, 38))
>SubproductNameForProductName : Symbol(SubproductNameForProductName, Decl(types.ts, 0, 35))
>P : Symbol(P, Decl(types.ts, 11, 31))

// ? E extends unknown // With unknown, the exception doesn't happen.
? EntitiesByProductName[P][E]
>EntitiesByProductName : Symbol(EntitiesByProductName, Decl(types.ts, 4, 9))
>P : Symbol(P, Decl(types.ts, 11, 31))
>E : Symbol(E, Decl(types.ts, 12, 38))

: never
: never

=== app.ts ===
import { SubproductNameForProductName, DiscriminatedUnion, ProductName } from './types'
>SubproductNameForProductName : Symbol(SubproductNameForProductName, Decl(app.ts, 0, 8))
>DiscriminatedUnion : Symbol(DiscriminatedUnion, Decl(app.ts, 0, 38))
>ProductName : Symbol(ProductName, Decl(app.ts, 0, 58))

export const bug = <P extends ProductName>() => {
>bug : Symbol(bug, Decl(app.ts, 2, 12))
>P : Symbol(P, Decl(app.ts, 2, 20))
>ProductName : Symbol(ProductName, Decl(app.ts, 0, 58))

const subproducts: DiscriminatedUnion<P, SubproductNameForProductName<P>>[] = []
>subproducts : Symbol(subproducts, Decl(app.ts, 3, 7))
>DiscriminatedUnion : Symbol(DiscriminatedUnion, Decl(app.ts, 0, 38))
>P : Symbol(P, Decl(app.ts, 2, 20))
>SubproductNameForProductName : Symbol(SubproductNameForProductName, Decl(app.ts, 0, 8))
>P : Symbol(P, Decl(app.ts, 2, 20))

subproducts.map((_: DiscriminatedUnion) => null)
>subproducts.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>subproducts : Symbol(subproducts, Decl(app.ts, 3, 7))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>_ : Symbol(_, Decl(app.ts, 4, 19))
>DiscriminatedUnion : Symbol(DiscriminatedUnion, Decl(app.ts, 0, 38))
}

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

=== types.ts ===
export type ProductName = 'a' | 'b'
>ProductName : ProductName
> : ^^^^^^^^^^^

export type SubproductNameForProductName<P extends ProductName> = P extends unknown
>SubproductNameForProductName : SubproductNameForProductName<P>
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

? keyof EntitiesByProductName[P]
: never

type EntitiesByProductName = {
>EntitiesByProductName : EntitiesByProductName
> : ^^^^^^^^^^^^^^^^^^^^^

a: { a1: { value: 'a-a1' } }
>a : { a1: { value: "a-a1"; }; }
> : ^^^^^^ ^^^
>a1 : { value: "a-a1"; }
> : ^^^^^^^^^ ^^^
>value : "a-a1"
> : ^^^^^^

b: { b1: { value: 'b-b1' } }
>b : { b1: { value: "b-b1"; }; }
> : ^^^^^^ ^^^
>b1 : { value: "b-b1"; }
> : ^^^^^^^^^ ^^^
>value : "b-b1"
> : ^^^^^^
}

export type DiscriminatedUnion<
>DiscriminatedUnion : DiscriminatedUnion<P, E>
> : ^^^^^^^^^^^^^^^^^^^^^^^^

P extends ProductName = ProductName,
E extends SubproductNameForProductName<P> = SubproductNameForProductName<P>,
> = P extends ProductName
? E extends SubproductNameForProductName<P>
// ? E extends unknown // With unknown, the exception doesn't happen.
? EntitiesByProductName[P][E]
: never
: never

=== app.ts ===
import { SubproductNameForProductName, DiscriminatedUnion, ProductName } from './types'
>SubproductNameForProductName : any
> : ^^^
>DiscriminatedUnion : any
> : ^^^
>ProductName : any
> : ^^^

export const bug = <P extends ProductName>() => {
>bug : <P extends ProductName>() => void
> : ^ ^^^^^^^^^ ^^^^^^^^^^^
><P extends ProductName>() => { const subproducts: DiscriminatedUnion<P, SubproductNameForProductName<P>>[] = [] subproducts.map((_: DiscriminatedUnion) => null)} : <P extends ProductName>() => void
> : ^ ^^^^^^^^^ ^^^^^^^^^^^

const subproducts: DiscriminatedUnion<P, SubproductNameForProductName<P>>[] = []
>subproducts : DiscriminatedUnion<P, SubproductNameForProductName<P>>[]
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>[] : never[]
> : ^^^^^^^

subproducts.map((_: DiscriminatedUnion) => null)
>subproducts.map((_: DiscriminatedUnion) => null) : null[]
> : ^^^^^^
>subproducts.map : <U>(callbackfn: (value: DiscriminatedUnion<P, SubproductNameForProductName<P>>, index: number, array: DiscriminatedUnion<P, SubproductNameForProductName<P>>[]) => U, thisArg?: any) => U[]
> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^
>subproducts : DiscriminatedUnion<P, SubproductNameForProductName<P>>[]
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>map : <U>(callbackfn: (value: DiscriminatedUnion<P, SubproductNameForProductName<P>>, index: number, array: DiscriminatedUnion<P, SubproductNameForProductName<P>>[]) => U, thisArg?: any) => U[]
> : ^ ^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^
>(_: DiscriminatedUnion) => null : (_: DiscriminatedUnion) => null
> : ^ ^^ ^^^^^^^^^
>_ : DiscriminatedUnion
> : ^^^^^^^^^^^^^^^^^^
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
=== Performance Stats ===
Assignability cache: 10,000
Type Count: 25,000
Instantiation count: 250,000 -> 500,000
Instantiation count: 500,000
Symbol count: 100,000

=== styledComponentsInstantiaionLimitNotReached.ts ===
Expand Down
31 changes: 31 additions & 0 deletions tests/cases/compiler/issue55217.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// @strict: true

// @filename: types.ts
export type ProductName = 'a' | 'b'

export type SubproductNameForProductName<P extends ProductName> = P extends unknown
? keyof EntitiesByProductName[P]
: never

type EntitiesByProductName = {
a: { a1: { value: 'a-a1' } }
b: { b1: { value: 'b-b1' } }
}

export type DiscriminatedUnion<
P extends ProductName = ProductName,
E extends SubproductNameForProductName<P> = SubproductNameForProductName<P>,
> = P extends ProductName
? E extends SubproductNameForProductName<P>
// ? E extends unknown // With unknown, the exception doesn't happen.
? EntitiesByProductName[P][E]
: never
: never

// @filename: app.ts
import { SubproductNameForProductName, DiscriminatedUnion, ProductName } from './types'

export const bug = <P extends ProductName>() => {
const subproducts: DiscriminatedUnion<P, SubproductNameForProductName<P>>[] = []
subproducts.map((_: DiscriminatedUnion) => null)
}