diff --git a/fs/expand_glob.ts b/fs/expand_glob.ts index 59c3b5a78f0e..7f9800f07a33 100644 --- a/fs/expand_glob.ts +++ b/fs/expand_glob.ts @@ -271,7 +271,7 @@ function comparePath(a: WalkEntry, b: WalkEntry): number { export async function* expandGlob( glob: string | URL, options?: ExpandGlobOptions, -): AsyncIterableIterator { +): AsyncGenerator { let { root, exclude = [], @@ -375,7 +375,9 @@ export async function* expandGlob( (entry: WalkEntry): boolean => !entry.isDirectory, ); } - yield* currentMatches; + for (const match of currentMatches) { + yield match; + } } /** @@ -428,7 +430,7 @@ export async function* expandGlob( export function* expandGlobSync( glob: string | URL, options?: ExpandGlobOptions, -): IterableIterator { +): Generator { let { root, exclude = [], @@ -530,7 +532,9 @@ export function* expandGlobSync( (entry: WalkEntry): boolean => !entry.isDirectory, ); } - yield* currentMatches; + for (const match of currentMatches) { + yield match; + } } const globEscapeChar = diff --git a/fs/expand_glob_test.ts b/fs/expand_glob_test.ts index f9386c58b8d4..d6d7b3258b3b 100644 --- a/fs/expand_glob_test.ts +++ b/fs/expand_glob_test.ts @@ -11,6 +11,7 @@ import { expandGlob, type ExpandGlobOptions, expandGlobSync, + type WalkEntry, } from "./expand_glob.ts"; async function expandGlobArray( @@ -464,3 +465,22 @@ Deno.test("expandGlobSync() finds directory with escaped brackets", function () [join("a[b]c", "foo")], ); }); + +Deno.test("expandGlobSync() preserves the Generator return type", () => { + // Regression test for https://github.com/denoland/std/issues/7099: + // the return type must be `Generator` so the ES2025 iterator + // helpers (`.map`, `.filter`, etc.) are exposed at the type level wherever + // the host TypeScript lib defines them. `Generator` requires the + // `.return()` / `.throw()` methods that `IterableIterator` leaves + // optional, so this assignment fails if the return type is widened back + // to `IterableIterator`. + const iter: Generator = expandGlobSync("*", EG_OPTIONS); + iter.return(undefined); +}); + +Deno.test("expandGlob() preserves the AsyncGenerator return type", async () => { + // Regression test for https://github.com/denoland/std/issues/7099 — see the + // sync counterpart for rationale. + const iter: AsyncGenerator = expandGlob("*", EG_OPTIONS); + await iter.return(undefined); +}); diff --git a/fs/walk.ts b/fs/walk.ts index 600e301cfa3f..be5b639d06a2 100644 --- a/fs/walk.ts +++ b/fs/walk.ts @@ -449,7 +449,7 @@ export type { WalkEntry }; export async function* walk( root: string | URL, options?: WalkOptions, -): AsyncIterableIterator { +): AsyncGenerator { let { maxDepth = Infinity, includeFiles = true, @@ -878,7 +878,7 @@ export async function* walk( export function* walkSync( root: string | URL, options?: WalkOptions, -): IterableIterator { +): Generator { let { maxDepth = Infinity, includeFiles = true, diff --git a/fs/walk_test.ts b/fs/walk_test.ts index ae507dd3ad25..26d7990159e3 100644 --- a/fs/walk_test.ts +++ b/fs/walk_test.ts @@ -1,5 +1,5 @@ // Copyright 2018-2026 the Deno authors. MIT license. -import { walk, type WalkOptions, walkSync } from "./walk.ts"; +import { walk, type WalkEntry, type WalkOptions, walkSync } from "./walk.ts"; import { assertArrayIncludes, assertEquals, @@ -338,6 +338,25 @@ Deno.test({ }, }); +Deno.test("walkSync() preserves the Generator return type", () => { + // Regression test for https://github.com/denoland/std/issues/7099: + // the return type must be `Generator` so the ES2025 iterator + // helpers (`.map`, `.filter`, etc.) are exposed at the type level wherever + // the host TypeScript lib defines them. `Generator` requires the + // `.return()` / `.throw()` methods that `IterableIterator` leaves + // optional, so this assignment fails if the return type is widened back + // to `IterableIterator`. + const iter: Generator = walkSync(testdataDir); + iter.return(undefined); +}); + +Deno.test("walk() preserves the AsyncGenerator return type", async () => { + // Regression test for https://github.com/denoland/std/issues/7099 — see the + // sync counterpart for rationale. + const iter: AsyncGenerator = walk(testdataDir); + await iter.return(undefined); +}); + Deno.test("walk() rejects with `Deno.errors.NotFound` when root is removed during execution", async () => { const tempDirPath = await Deno.makeTempDir({ prefix: "deno_std_walk_",