Fixed an issue with "slow" sync iteration types spoiling cached value for async ones (#62676)

This commit is contained in:
Mateusz Burzyński 2025-10-29 23:56:21 +01:00 committed by GitHub
parent 11ee01b22d
commit 2a90a739c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 285 additions and 11 deletions

View File

@ -45924,7 +45924,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
let noCache = false;
if (use & IterationUse.AllowsAsyncIterablesFlag) {
const iterationTypes = getIterationTypesOfIterableCached(type, asyncIterationTypesResolver) ||
let iterationTypes = getIterationTypesOfIterableCached(type, asyncIterationTypesResolver) ||
getIterationTypesOfIterableFast(type, asyncIterationTypesResolver);
if (iterationTypes) {
if (iterationTypes === noIterationTypes && errorNode) {
@ -45937,6 +45937,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
iterationTypes;
}
}
iterationTypes = getIterationTypesOfIterableSlow(type, asyncIterationTypesResolver, errorNode, errorOutputContainer, noCache);
if (iterationTypes !== noIterationTypes) {
return iterationTypes;
}
}
if (use & IterationUse.AllowsSyncIterablesFlag) {
@ -45960,17 +45964,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
}
}
}
if (use & IterationUse.AllowsAsyncIterablesFlag) {
const iterationTypes = getIterationTypesOfIterableSlow(type, asyncIterationTypesResolver, errorNode, errorOutputContainer, noCache);
if (iterationTypes !== noIterationTypes) {
return iterationTypes;
}
}
if (use & IterationUse.AllowsSyncIterablesFlag) {
let iterationTypes = getIterationTypesOfIterableSlow(type, syncIterationTypesResolver, errorNode, errorOutputContainer, noCache);
iterationTypes = getIterationTypesOfIterableSlow(type, syncIterationTypesResolver, errorNode, errorOutputContainer, noCache);
if (iterationTypes !== noIterationTypes) {
if (use & IterationUse.AllowsAsyncIterablesFlag) {
iterationTypes = getAsyncFromSyncIterationTypes(iterationTypes, errorNode);

View File

@ -0,0 +1,119 @@
//// [tests/cases/compiler/forAwaitForIntersection1.ts] ////
=== forAwaitForIntersection1.ts ===
type Stream1<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
>Stream1 : Symbol(Stream1, Decl(forAwaitForIntersection1.ts, 0, 0))
>T_Sync : Symbol(T_Sync, Decl(forAwaitForIntersection1.ts, 0, 13))
>T_Async : Symbol(T_Async, Decl(forAwaitForIntersection1.ts, 0, 20))
>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --))
>T_Sync : Symbol(T_Sync, Decl(forAwaitForIntersection1.ts, 0, 13))
>AsyncIterable : Symbol(AsyncIterable, Decl(lib.es2018.asynciterable.d.ts, --, --))
>T_Async : Symbol(T_Async, Decl(forAwaitForIntersection1.ts, 0, 20))
class A1 {}
>A1 : Symbol(A1, Decl(forAwaitForIntersection1.ts, 0, 74))
class B1 {}
>B1 : Symbol(B1, Decl(forAwaitForIntersection1.ts, 2, 11))
async function loop1(stream: Stream1<A1, B1>) {
>loop1 : Symbol(loop1, Decl(forAwaitForIntersection1.ts, 3, 11))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 5, 21))
>Stream1 : Symbol(Stream1, Decl(forAwaitForIntersection1.ts, 0, 0))
>A1 : Symbol(A1, Decl(forAwaitForIntersection1.ts, 0, 74))
>B1 : Symbol(B1, Decl(forAwaitForIntersection1.ts, 2, 11))
for await (const b of stream) {}
>b : Symbol(b, Decl(forAwaitForIntersection1.ts, 6, 18))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 5, 21))
}
type Stream2<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
>Stream2 : Symbol(Stream2, Decl(forAwaitForIntersection1.ts, 7, 1))
>T_Sync : Symbol(T_Sync, Decl(forAwaitForIntersection1.ts, 9, 13))
>T_Async : Symbol(T_Async, Decl(forAwaitForIntersection1.ts, 9, 20))
>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --))
>T_Sync : Symbol(T_Sync, Decl(forAwaitForIntersection1.ts, 9, 13))
>AsyncIterable : Symbol(AsyncIterable, Decl(lib.es2018.asynciterable.d.ts, --, --))
>T_Async : Symbol(T_Async, Decl(forAwaitForIntersection1.ts, 9, 20))
class A2 {}
>A2 : Symbol(A2, Decl(forAwaitForIntersection1.ts, 9, 74))
class B2 {}
>B2 : Symbol(B2, Decl(forAwaitForIntersection1.ts, 11, 11))
async function loop2(stream: Stream2<A2, B2>) {
>loop2 : Symbol(loop2, Decl(forAwaitForIntersection1.ts, 12, 11))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 14, 21))
>Stream2 : Symbol(Stream2, Decl(forAwaitForIntersection1.ts, 7, 1))
>A2 : Symbol(A2, Decl(forAwaitForIntersection1.ts, 9, 74))
>B2 : Symbol(B2, Decl(forAwaitForIntersection1.ts, 11, 11))
for (const a of stream) {}
>a : Symbol(a, Decl(forAwaitForIntersection1.ts, 15, 12))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 14, 21))
}
type Stream3<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
>Stream3 : Symbol(Stream3, Decl(forAwaitForIntersection1.ts, 16, 1))
>T_Sync : Symbol(T_Sync, Decl(forAwaitForIntersection1.ts, 18, 13))
>T_Async : Symbol(T_Async, Decl(forAwaitForIntersection1.ts, 18, 20))
>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --))
>T_Sync : Symbol(T_Sync, Decl(forAwaitForIntersection1.ts, 18, 13))
>AsyncIterable : Symbol(AsyncIterable, Decl(lib.es2018.asynciterable.d.ts, --, --))
>T_Async : Symbol(T_Async, Decl(forAwaitForIntersection1.ts, 18, 20))
class A3 {}
>A3 : Symbol(A3, Decl(forAwaitForIntersection1.ts, 18, 74))
class B3 {}
>B3 : Symbol(B3, Decl(forAwaitForIntersection1.ts, 20, 11))
async function loop3(stream: Stream3<A3, B3>) {
>loop3 : Symbol(loop3, Decl(forAwaitForIntersection1.ts, 21, 11))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 23, 21))
>Stream3 : Symbol(Stream3, Decl(forAwaitForIntersection1.ts, 16, 1))
>A3 : Symbol(A3, Decl(forAwaitForIntersection1.ts, 18, 74))
>B3 : Symbol(B3, Decl(forAwaitForIntersection1.ts, 20, 11))
for await (const b of stream) {}
>b : Symbol(b, Decl(forAwaitForIntersection1.ts, 24, 18))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 23, 21))
for (const a of stream) {}
>a : Symbol(a, Decl(forAwaitForIntersection1.ts, 26, 12))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 23, 21))
}
type Stream4<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
>Stream4 : Symbol(Stream4, Decl(forAwaitForIntersection1.ts, 27, 1))
>T_Sync : Symbol(T_Sync, Decl(forAwaitForIntersection1.ts, 29, 13))
>T_Async : Symbol(T_Async, Decl(forAwaitForIntersection1.ts, 29, 20))
>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --))
>T_Sync : Symbol(T_Sync, Decl(forAwaitForIntersection1.ts, 29, 13))
>AsyncIterable : Symbol(AsyncIterable, Decl(lib.es2018.asynciterable.d.ts, --, --))
>T_Async : Symbol(T_Async, Decl(forAwaitForIntersection1.ts, 29, 20))
class A4 {}
>A4 : Symbol(A4, Decl(forAwaitForIntersection1.ts, 29, 74))
class B4 {}
>B4 : Symbol(B4, Decl(forAwaitForIntersection1.ts, 31, 11))
// verify that resolving sync iteration first doesn't spoil the type for async iteration
async function loop4(stream: Stream4<A4, B4>) {
>loop4 : Symbol(loop4, Decl(forAwaitForIntersection1.ts, 32, 11))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 35, 21))
>Stream4 : Symbol(Stream4, Decl(forAwaitForIntersection1.ts, 27, 1))
>A4 : Symbol(A4, Decl(forAwaitForIntersection1.ts, 29, 74))
>B4 : Symbol(B4, Decl(forAwaitForIntersection1.ts, 31, 11))
for (const a of stream) {}
>a : Symbol(a, Decl(forAwaitForIntersection1.ts, 36, 12))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 35, 21))
for await (const b of stream) {}
>b : Symbol(b, Decl(forAwaitForIntersection1.ts, 38, 18))
>stream : Symbol(stream, Decl(forAwaitForIntersection1.ts, 35, 21))
}

View File

@ -0,0 +1,115 @@
//// [tests/cases/compiler/forAwaitForIntersection1.ts] ////
=== forAwaitForIntersection1.ts ===
type Stream1<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
>Stream1 : Stream1<T_Sync, T_Async>
> : ^^^^^^^^^^^^^^^^^^^^^^^^
class A1 {}
>A1 : A1
> : ^^
class B1 {}
>B1 : B1
> : ^^
async function loop1(stream: Stream1<A1, B1>) {
>loop1 : (stream: Stream1<A1, B1>) => Promise<void>
> : ^ ^^ ^^^^^^^^^^^^^^^^^^
>stream : Stream1<A1, B1>
> : ^^^^^^^^^^^^^^^
for await (const b of stream) {}
>b : B1
> : ^^
>stream : Stream1<A1, B1>
> : ^^^^^^^^^^^^^^^
}
type Stream2<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
>Stream2 : Stream2<T_Sync, T_Async>
> : ^^^^^^^^^^^^^^^^^^^^^^^^
class A2 {}
>A2 : A2
> : ^^
class B2 {}
>B2 : B2
> : ^^
async function loop2(stream: Stream2<A2, B2>) {
>loop2 : (stream: Stream2<A2, B2>) => Promise<void>
> : ^ ^^ ^^^^^^^^^^^^^^^^^^
>stream : Stream2<A2, B2>
> : ^^^^^^^^^^^^^^^
for (const a of stream) {}
>a : A2
> : ^^
>stream : Stream2<A2, B2>
> : ^^^^^^^^^^^^^^^
}
type Stream3<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
>Stream3 : Stream3<T_Sync, T_Async>
> : ^^^^^^^^^^^^^^^^^^^^^^^^
class A3 {}
>A3 : A3
> : ^^
class B3 {}
>B3 : B3
> : ^^
async function loop3(stream: Stream3<A3, B3>) {
>loop3 : (stream: Stream3<A3, B3>) => Promise<void>
> : ^ ^^ ^^^^^^^^^^^^^^^^^^
>stream : Stream3<A3, B3>
> : ^^^^^^^^^^^^^^^
for await (const b of stream) {}
>b : B3
> : ^^
>stream : Stream3<A3, B3>
> : ^^^^^^^^^^^^^^^
for (const a of stream) {}
>a : A3
> : ^^
>stream : Stream3<A3, B3>
> : ^^^^^^^^^^^^^^^
}
type Stream4<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
>Stream4 : Stream4<T_Sync, T_Async>
> : ^^^^^^^^^^^^^^^^^^^^^^^^
class A4 {}
>A4 : A4
> : ^^
class B4 {}
>B4 : B4
> : ^^
// verify that resolving sync iteration first doesn't spoil the type for async iteration
async function loop4(stream: Stream4<A4, B4>) {
>loop4 : (stream: Stream4<A4, B4>) => Promise<void>
> : ^ ^^ ^^^^^^^^^^^^^^^^^^
>stream : Stream4<A4, B4>
> : ^^^^^^^^^^^^^^^
for (const a of stream) {}
>a : A4
> : ^^
>stream : Stream4<A4, B4>
> : ^^^^^^^^^^^^^^^
for await (const b of stream) {}
>b : B4
> : ^^
>stream : Stream4<A4, B4>
> : ^^^^^^^^^^^^^^^
}

View File

@ -0,0 +1,45 @@
// @strict: true
// @target: es2018
// @lib: esnext
// @noEmit: true
type Stream1<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
class A1 {}
class B1 {}
async function loop1(stream: Stream1<A1, B1>) {
for await (const b of stream) {}
}
type Stream2<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
class A2 {}
class B2 {}
async function loop2(stream: Stream2<A2, B2>) {
for (const a of stream) {}
}
type Stream3<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
class A3 {}
class B3 {}
async function loop3(stream: Stream3<A3, B3>) {
for await (const b of stream) {}
for (const a of stream) {}
}
type Stream4<T_Sync, T_Async> = Iterable<T_Sync> & AsyncIterable<T_Async>;
class A4 {}
class B4 {}
// verify that resolving sync iteration first doesn't spoil the type for async iteration
async function loop4(stream: Stream4<A4, B4>) {
for (const a of stream) {}
for await (const b of stream) {}
}