-
Notifications
You must be signed in to change notification settings - Fork 5.1k
fix(types): correct FormData iterator return types to include File #27195
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| import { expect, test } from "bun:test"; | ||
|
|
||
| test("FormData.values() returns File objects, not just strings", () => { | ||
| const fd = new FormData(); | ||
| const file = new File(["content"], "test.txt", { type: "text/plain" }); | ||
| fd.append("textField", "hello"); | ||
| fd.append("fileField", file); | ||
|
|
||
| const values = [...fd.values()]; | ||
| expect(values).toHaveLength(2); | ||
| expect(values[0]).toBe("hello"); | ||
| expect(values[1]).toBeInstanceOf(File); | ||
| }); | ||
|
Check warning on line 13 in test/regression/issue/27194.test.ts
|
||
|
Comment on lines
+1
to
+13
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π‘ This runtime test doesn't guard the actual change in this PR β the PR only edits Extended reasoning...What's wrongThis PR's only functional change is to two type annotations in Why existing guidance flags thisTwo repo conventions apply directly:
Separately, CLAUDE.md:66 and test/CLAUDE.md:153 reserve Step-by-step proof
Suggested fixReplace the runtime test with a type-level assertion in import { expectType } from "./utilities";
// ...
const fd = new FormData();
for (const v of fd.values()) expectType<Bun.FormDataEntryValue>(v);
for (const [k, v] of fd.entries()) {
expectType<string>(k);
expectType<Bun.FormDataEntryValue>(v);
}This will fail |
||
|
|
||
| test("FormData.entries() returns File objects in value position", () => { | ||
| const fd = new FormData(); | ||
| const file = new File(["content"], "test.txt", { type: "text/plain" }); | ||
| fd.append("textField", "hello"); | ||
| fd.append("fileField", file); | ||
|
|
||
| const entries = [...fd.entries()]; | ||
| expect(entries).toHaveLength(2); | ||
| expect(entries[0]).toEqual(["textField", "hello"]); | ||
| expect(entries[1][0]).toBe("fileField"); | ||
| expect(entries[1][1]).toBeInstanceOf(File); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
π£ While you're fixing the iterator return types here, consider also adding
[Symbol.iterator](): IterableIterator<[string, Bun.FormDataEntryValue]>;β the runtime aliasesSymbol.iteratortoentries()(JSDOMFormData.cpp), but bun-types doesn't declare it, so withoutlib.domfor (const [k, v] of fd)fails to type-check. This is a pre-existing gap, not something this PR introduced, but it's a one-line addition adjacent to the lines you're already touching and falls under the same "FormData iterator return types" concern.Extended reasoning...
What's missing
The
interface FormDataat packages/bun-types/globals.d.ts:1664-1685 declareskeys(),values(), andentries(), but has no[Symbol.iterator]()declaration. Per the WHATWG spec,FormDatais iterable and its default iterator yields the same[string, FormDataEntryValue]pairs asentries(). Bun's runtime implements exactly that βJSDOMFormData::finishCreationdoesputDirect(vm, vm.propertyNames->iteratorSymbol, getDirect(vm, builtinNames.entriesPublicName())), aliasing@@iteratorto theentriesfunction.How it manifests
Without
lib.domloaded (the default for Bun projects, which use"lib": ["ESNext"]), TypeScript sees only the bun-typesFormDatainterface. Since that interface has no[Symbol.iterator](), code like:fails with TS2488 ("Type 'FormData' must have a 'Symbol.iterator' method that returns an iterator"), and
[...fd]likewise fails. The runtime supports this perfectly fine β only the types are missing.Why nothing else covers it
A grep across packages/bun-types shows this is the only
interface FormDatadeclaration, andSymbol.iteratoris declared forCookieMapandsqlite.Statementbut notFormData. bun-types depends only on@types/node(which provides no globalFormData) and importsundici-typesonly forEventSource, so there's no other source of a[Symbol.iterator]forFormDatawhenlib.domis absent. The existing type fixture at test/integration/bun-types/fixture/globals.ts testsentries()/keys()/values()but notfor...of, which is why this gap hasn't been caught.Step-by-step proof
@types/bunwith"lib": ["ESNext"](nolib.dom) β the recommended Bun setup.FormDatato packages/bun-types/globals.d.ts:1664-1685.entries(): IterableIterator<[string, Bun.FormDataEntryValue]>(after this PR) but no[Symbol.iterator]().for (const [name, value] of fd) { ... }.FormDatais not declared iterable.@@iterator = entries.Relevance to this PR
This is pre-existing β the PR didn't introduce or worsen it. But the PR's stated goal is to "correct FormData iterator return types to include File", and the default iterator is the most common way people iterate
FormData. Fixingvalues()/entries()while leavingfor...ofun-typed is an incomplete fix for the same conceptual issue, and the addition is one line right next to the lines being changed.Suggested fix
This matches how
lib.dom.iterable.d.tsdeclares it, so whenlib.domis loaded the merged interface remains consistent (both declare the same signature shape). Not a blocker β just a suggestion for completeness while you're in this exact spot.