From 31290b90967c6c023c294449e348ce1879cafa82 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sat, 24 Oct 2026 06:48:05 +0000 Subject: [PATCH] bun-types: declare S3File.name as string and remove S3File.readable Two members of `interface S3File` did not match the run time. #15740 took both from BunFile. `name` was `name?: string`. The name of an S3File is always a string: the path given to file(). `new File([bytes], s3file.name)` failed under strict with TS2345. `readable` was declared, and no S3File has it: `"readable" in s3file` is false. Code that reads it compiles and gets `undefined`. The declaration is gone, so that code now fails with TS2339. `stream()` replaces it. #20879 removed the same member from BunFile. One case stays as it is: slice() on a blob whose size is 0 returns an empty plain Blob, not an S3File. That Blob has no name, bucket or presign. The JSDoc of slice() now says so. BunFile.name stays optional: a file opened by file descriptor has no name. --- packages/bun-types/s3.d.ts | 7 ++++--- test/integration/bun-types/fixture/fetch.ts | 2 +- test/integration/bun-types/fixture/s3.ts | 6 +++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/packages/bun-types/s3.d.ts b/packages/bun-types/s3.d.ts index 1d1d62ec494c..3aee4025a402 100644 --- a/packages/bun-types/s3.d.ts +++ b/packages/bun-types/s3.d.ts @@ -507,7 +507,9 @@ declare module "bun" { * @param begin Starting byte offset * @param end Ending byte offset (exclusive) * @param contentType Optional MIME type for the slice - * @returns A new S3File representing the specified range + * @returns A new S3File representing the specified range. A slice of a + * zero-length slice (`file.slice(0, 0).slice()`) is an empty `Blob` with + * no `name`, `bucket` or `presign`. * * @example * ```ts @@ -618,7 +620,6 @@ declare module "bun" { * } * ``` */ - readonly readable: ReadableStream>; stream(): ReadableStream>; /** @@ -630,7 +631,7 @@ declare module "bun" { * console.log(file.name); // "folder/image.jpg" * ``` */ - readonly name?: string; + readonly name: string; /** * The bucket name containing the file. diff --git a/test/integration/bun-types/fixture/fetch.ts b/test/integration/bun-types/fixture/fetch.ts index 86a8abbf7444..eca0167ef331 100644 --- a/test/integration/bun-types/fixture/fetch.ts +++ b/test/integration/bun-types/fixture/fetch.ts @@ -16,8 +16,8 @@ fetch("https://example.com", { body: Bun.file("hey").stream() }); fetch("https://example.com", { body: new Response("bun").body }); fetch("https://example.com", { body: Bun.s3.file("hey") }); fetch("https://example.com", { body: Bun.s3.file("hey").stream() }); +// @ts-expect-error - an S3File has no `readable`, use `stream()` fetch("https://example.com", { body: Bun.s3.file("hey").readable }); - async function* asyncGenerator() { yield "chunk1"; yield "chunk2"; diff --git a/test/integration/bun-types/fixture/s3.ts b/test/integration/bun-types/fixture/s3.ts index 7f9186bce414..d11996f02228 100644 --- a/test/integration/bun-types/fixture/s3.ts +++ b/test/integration/bun-types/fixture/s3.ts @@ -1,4 +1,5 @@ import { s3 } from "bun"; +import { expectType } from "./utilities"; async function doFileOps(file: Bun.S3File) { console.log(file.bucket); @@ -6,12 +7,15 @@ async function doFileOps(file: Bun.S3File) { console.log(file.presign({ expiresIn: 1, method: "PUT" })); console.log(file.type); + expectType(file.name).is(); + new File([await file.bytes()], file.name, { type: file.type }); + await file.json(); await file.arrayBuffer(); await file.delete(); await file.formData(); - for await (const chunk of file.readable) { + for await (const chunk of file.stream()) { console.log(chunk); } }