Skip to content

fix: use explicit never[] type for MessageBox empty constants#39675

Open
Salvatoree07 wants to merge 3 commits into
RocketChat:developfrom
Salvatoree07:develop
Open

fix: use explicit never[] type for MessageBox empty constants#39675
Salvatoree07 wants to merge 3 commits into
RocketChat:developfrom
Salvatoree07:develop

Conversation

@Salvatoree07
Copy link
Copy Markdown

@Salvatoree07 Salvatoree07 commented Mar 16, 2026

Proposed changes (including videos or screenshots)

Instead of allowing TypeScript to infer an ambiguous type for a constant that is intentionally empty, I have explicitly typed it as never[]. This is the most type-safe way to handle constants that are meant to represent an empty state, preventing any accidental insertion of data and satisfying the linter/compiler requirements.

Issue(s)

#39674

Steps to test or reproduce

  1. Open the MessageBox component in a scenario where it utilizes a default empty constant.
  2. Observe that without this fix, the compiler might complain about the array being untyped or implicitly any[].
  3. Verify that the build now passes with strict type checking enabled.

Further comments

I chose this solution because this specific array is intended to be a constant empty fallback. Using never[] is more accurate than a generic type in this context, as it reinforces the intent that no elements should ever exist in this specific array instance.

Summary by CodeRabbit

  • Refactor
    • Improved internal type safety in the message composer’s formatting toolbar and fallback handling. No visible UI or behavior changes for end users; this reduces the risk of type-related issues and helps maintain stability in message composition.

@Salvatoree07 Salvatoree07 requested a review from a team as a code owner March 16, 2026 20:36
@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented Mar 16, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 16, 2026

⚠️ No Changeset found

Latest commit: 87bdfaa

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 66393be0-de8b-4411-a45c-7cdd84339ac6

📥 Commits

Reviewing files that changed from the base of the PR and between 2464f50 and 87bdfaa.

📒 Files selected for processing (2)
  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
  • apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx
  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
🧠 Learnings (4)
📓 Common learnings
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39304
File: packages/ui-contexts/src/ActionManagerContext.ts:26-26
Timestamp: 2026-03-04T14:16:49.202Z
Learning: In `packages/ui-contexts/src/ActionManagerContext.ts` (TypeScript, RocketChat/Rocket.Chat), the `disposeView` method in `IActionManager` uses an intentionally explicit union `UiKit.ModalView['id'] | UiKit.BannerView['viewId'] | UiKit.ContextualBarView['id']` to document which view types are accepted, even though all constituents resolve to the same primitive. The inline `// eslint-disable-next-line typescript-eslint/no-duplicate-type-constituents` comment is intentional and should not be flagged or removed.
📚 Learning: 2025-11-19T18:20:07.720Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: packages/i18n/src/locales/en.i18n.json:918-921
Timestamp: 2025-11-19T18:20:07.720Z
Learning: Repo: RocketChat/Rocket.Chat — i18n/formatting
Learning: This repository uses a custom message formatting parser in UI blocks/messages; do not assume standard Markdown rules. For keys like Call_ended_bold, Call_not_answered_bold, Call_failed_bold, and Call_transferred_bold in packages/i18n/src/locales/en.i18n.json, retain the existing single-asterisk emphasis unless maintainers request otherwise.

Applied to files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
📚 Learning: 2026-03-20T13:51:20.280Z
Learnt from: ggazzo
Repo: RocketChat/Rocket.Chat PR: 39553
File: apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts:179-181
Timestamp: 2026-03-20T13:51:20.280Z
Learning: In `apps/meteor/app/integrations/server/methods/incoming/updateIncomingIntegration.ts`, the truthiness guards `...(integration.avatar && { avatar })`, `...(integration.emoji && { emoji })`, `...(integration.alias && { alias })`, and `...(integration.script && { script })` in the `$set` payload of `updateIncomingIntegration` are intentional. Empty-string values for these fields should NOT overwrite the stored value — only truthy values are persisted. Do not flag these as bugs preventing explicit clears.

Applied to files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
🔇 Additional comments (2)
apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx (1)

78-79: LGTM!

The module-level EMPTY_FORMATTER constant with readonly FormattingButton[] type is a well-chosen approach:

  • Maintains referential stability for useSyncExternalStore (avoids re-renders from new array allocations)
  • Provides correct typing that aligns with the expected FormattingButton[] shape downstream
  • Uses readonly to prevent accidental mutation, addressing the reviewer's suggestion over never[]
apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx (1)

13-13: LGTM!

The readonly modifier correctly propagates the immutability constraint from the parent component. The spread operator on line 21 appropriately creates a mutable copy for the splice operation, preserving the original array's immutability.


Walkthrough

Replaced a local fallback empty-array with a module-level readonly EMPTY_FORMATTER and tightened the items prop type to readonly FormattingButton[] in the message composer components; runtime behavior unchanged, only TypeScript typings were adjusted.

Changes

Cohort / File(s) Summary
Type annotation updates
apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx, apps/meteor/client/views/room/composer/messageBox/MessageBoxFormattingToolbar/MessageBoxFormattingToolbar.tsx
Replaced mutable/fallback any[] with a module-level readonly FormattingButton[] constant and updated MessageBoxFormattingToolbarProps.items from FormattingButton[] to readonly FormattingButton[]. Changes affect TypeScript inference only; no runtime logic changes.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

type: chore

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title claims the PR uses 'explicit never[] type' but the actual changes show the array is typed as 'readonly FormattingButton[]', not 'never[]'. The title is misleading about the implementation approach. Update the title to accurately reflect the changes, e.g., 'fix: use readonly FormattingButton[] for MessageBox empty constants' or similar, to match the actual type used.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can validate your CodeRabbit configuration file in your editor.

If your editor has YAML language server, you can enable auto-completion and validation by adding # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json at the top of your CodeRabbit configuration file.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 1 file


Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx (1)

78-79: LGTM on the never[] type annotation.

Using never[] for a constant empty array that should never hold elements is the correct TypeScript pattern. It prevents accidental insertion and satisfies strict type checking.

Consider a more descriptive variable name (optional):

The single-letter name a is not self-documenting. A name like EMPTY_ARRAY or emptyFormatterArray would better convey its purpose and immutability intent.

♻️ Optional naming improvement
-const a: never[] = [];
-const getEmptyArray = () => a;
+const EMPTY_ARRAY: never[] = [];
+const getEmptyArray = () => EMPTY_ARRAY;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx` around
lines 78 - 79, Rename the cryptic constant a to a descriptive, immutable name
and propagate that change where used: replace the variable a with something like
EMPTY_ARRAY or emptyFormatterArray and update the getEmptyArray function to
return the new name (symbols to change: a and getEmptyArray in MessageBox.tsx)
so the intent (constant never[] empty array) is clear while preserving the
never[] type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx`:
- Around line 78-79: Rename the cryptic constant a to a descriptive, immutable
name and propagate that change where used: replace the variable a with something
like EMPTY_ARRAY or emptyFormatterArray and update the getEmptyArray function to
return the new name (symbols to change: a and getEmptyArray in MessageBox.tsx)
so the intent (constant never[] empty array) is clear while preserving the
never[] type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 197d3646-40f1-4683-94b6-a519ba7c7a36

📥 Commits

Reviewing files that changed from the base of the PR and between b93ddd6 and 781e04c.

📒 Files selected for processing (1)
  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: cubic · AI code reviewer
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: smirk-dev
Repo: RocketChat/Rocket.Chat PR: 39625
File: apps/meteor/app/api/server/v1/push.ts:85-97
Timestamp: 2026-03-14T14:58:58.834Z
Learning: In RocketChat/Rocket.Chat, the `push.token` POST/DELETE endpoints in `apps/meteor/app/api/server/v1/push.ts` were already migrated to the chained router API pattern on `develop` prior to PR `#39625`. `cleanTokenResult` (which strips `authToken` and returns `PushTokenResult`) and `isPushTokenPOSTProps`/`isPushTokenDELETEProps` validators already exist on `develop`. PR `#39625` only migrates `push.get` and `push.info` to the chained pattern. Do not flag `cleanTokenResult` or `PushTokenResult` as newly introduced behavior-breaking changes when reviewing this PR.
📚 Learning: 2026-03-03T11:11:48.541Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 39230
File: apps/meteor/app/api/server/v1/chat.ts:214-222
Timestamp: 2026-03-03T11:11:48.541Z
Learning: In apps/meteor/server/lib/moderation/reportMessage.ts, the reportMessage function validates that description is not empty or whitespace-only with `if (!description.trim())`. When migrating the chat.reportMessage endpoint to OpenAPI, adding minLength validation to the schema preserves this existing behavior.

Applied to files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
📚 Learning: 2026-03-04T14:16:49.202Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 39304
File: packages/ui-contexts/src/ActionManagerContext.ts:26-26
Timestamp: 2026-03-04T14:16:49.202Z
Learning: In `packages/ui-contexts/src/ActionManagerContext.ts` (TypeScript, RocketChat/Rocket.Chat), the `disposeView` method in `IActionManager` uses an intentionally explicit union `UiKit.ModalView['id'] | UiKit.BannerView['viewId'] | UiKit.ContextualBarView['id']` to document which view types are accepted, even though all constituents resolve to the same primitive. The inline `// eslint-disable-next-line typescript-eslint/no-duplicate-type-constituents` comment is intentional and should not be flagged or removed.

Applied to files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
📚 Learning: 2026-02-25T20:10:16.987Z
Learnt from: ahmed-n-abdeltwab
Repo: RocketChat/Rocket.Chat PR: 38913
File: packages/ddp-client/src/legacy/types/SDKLegacy.ts:34-34
Timestamp: 2026-02-25T20:10:16.987Z
Learning: In the RocketChat/Rocket.Chat monorepo, packages/ddp-client and apps/meteor do not use TypeScript project references. Module augmentations in apps/meteor (e.g., declare module 'rocket.chat/rest-typings') are not visible when compiling packages/ddp-client in isolation, which is why legacy SDK methods that depend on OperationResult types for OpenAPI-migrated endpoints must remain commented out.

Applied to files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/client/views/room/composer/messageBox/MessageBox.tsx

@Hardik-Kumar20
Copy link
Copy Markdown

@Salvatoree07
Nice improvement in removing 'any[]'
I have one concern using 'never[]' might be too restrictive here. This array is used as a fallback for 'formatters' which are expected to be of type 'FormattingButton[]'.
Using 'never[]' implies that the array will never contain any values, which doesn't align with how 'formatters' is used and may lead to type inconsistencies.

A better approach could be to use a properly typed immutable fallback.
const EMPTY_FORMATTERS: readonly FormattingButton[] = [];
This ensures no 'any[]' leakage, Correct type inference, Prevents mutation, Matches the expected data shape.

Happy to help update the PR if needed.

@Salvatoree07
Copy link
Copy Markdown
Author

Hi, thanks for the feedback! You're absolutely right. Using never[] would be technically correct for an empty set but too restrictive for the formatters consumer. In fact, I've switched to readonly FormattingButton[].

I also updated the type of the items attribute in MessageBoxFormattingToolbar by adding the readonly modifier, as these items should not be modified. All the changes are included in the latest commit of this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants