Skip to content
Draft
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
10 changes: 8 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23344,6 +23344,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 @@ -24074,9 +24081,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
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
> : ^^^^^^^^^^^^^^^^^^
}

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)
}