The Problem
When building Discord bots with discord.js, developers can create modals with optional FileUpload components that allow users to attach files. However, a type safety gap existed between TypeScript’s compile-time checks and the actual runtime behavior.
The TypeScript type definitions marked the attachments field as required in the FileUploadModalData interface. In reality, when a user submits a modal without uploading any files, component.attachments is undefined at runtime. This mismatch meant developers could write code that TypeScript considered perfectly valid, only to have it crash in production.
Consider this seemingly safe code pattern:
for (const field of ctx.fields.components.filter((c) => c.type === ComponentType.Label)) {
const comp = field.component;
if (comp.type === ComponentType.FileUpload) {
// TypeScript says this is fine, but it throws at runtime!
const fieldAttachments = comp.attachments.values().toArray();
}
}
Calling comp.attachments.values() without a null check throws TypeError: undefined is not an object when the user doesn’t upload a file. Developers had to discover this the hard way and manually add optional chaining (comp.attachments?.values()) as a workaround.
The Solution
The fix was straightforward: update the type definition to accurately reflect the runtime behavior by making the attachments property optional.
// Before - incorrectly marked as required
attachments: ReadonlyCollection<Snowflake, Attachment>;
// After - correctly marked as optional
attachments?: ReadonlyCollection<Snowflake, Attachment>;
This single-character change (?) enables TypeScript to catch unsafe access patterns during compilation. Developers now get proper IDE warnings and compile-time errors when accessing attachments without first checking if it exists.
Files Changed
| File | Changes |
|---|---|
packages/discord.js/typings/index.d.ts | +1 −1 |
Review Discussion
During the review process, maintainer vladfrangu raised an interesting architectural question: “Is there a reason we don’t just always create a collection?” Team member Jiralite suggested that if the API receives an empty array, it should create an empty collection instead of undefined. This indicates potential future improvements to the discord.js codebase that could eliminate this edge case entirely.
Timeline
| Date | Event |
|---|---|
| 2025-12-13 | Issue #11359 reported by The-LukeZ |
| 2025-12-13 | Forked repo, implemented fix, submitted PR #11363 |
| 2025-12-17 | Received first approval from almeidx |
| 2025-12-18 | All CI checks passing |
| 2026-01-02 | Additional review discussion from maintainers |
Impact
This fix improves type safety for all discord.js developers working with modal file uploads (v14.25.1+). By aligning TypeScript types with actual runtime behavior, it helps catch bugs at compile time rather than in production, following TypeScript’s core philosophy of providing strong type guarantees.