refactor(types): typed send helpers replace 16 as any casts (#210)

- sendRoomMessage (composer's fire-and-forget sends: text, location, voice,
  files, GIFs) and sendRoomEvent (polls, poll responses/ends, forwards,
  reactions, edits) in utils/room.ts carry the one cast each needs
  (`keyof TimelineEvents` / `RoomMessageEventContent`, no `any`).
- sendRoomMessage also swallows the rejected promise: a failed send already
  shows on the local echo (Failed to send + Retry, or the consent prompt), so
  it no longer surfaces as an unhandled error in the console.
- getAccountData narrows to `keyof AccountDataEvents`; ForwardMessageDialog's
  guard now narrows `contentToSend` itself (same behaviour).
- `as any` 39 → 23; eslint warnings 46 → 36, ratchet tightened to 36.

Verified in Chromium on a local Synapse: a text message, a quick reaction and
an edit all reach the server with the right content; a consent-blocked send
no longer logs an unhandled MatrixError. 1219 unit tests pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
Lotus CI
2026-09-24 21:47:44 -04:00
committed by jared
co-authored by Claude Opus 5.5
parent 39589b40f3
commit e39714a6e0
9 changed files with 93 additions and 35 deletions
+38 -1
View File
@@ -1,9 +1,11 @@
import { IconName, IconSrc } from 'folds';
import {
AccountDataEvents,
EventTimeline,
EventTimelineSet,
EventType,
IContent,
IMentions,
IPowerLevelsContent,
IPushRule,
@@ -17,8 +19,10 @@ import {
RelationType,
Room,
RoomMember,
TimelineEvents,
} from 'matrix-js-sdk';
import { CryptoBackend } from 'matrix-js-sdk/lib/common-crypto/CryptoBackend';
import type { RoomMessageEventContent } from 'matrix-js-sdk/lib/@types/events';
import { AccountDataEvent } from '../../types/matrix/accountData';
import {
IRoomCreateContent,
@@ -62,10 +66,43 @@ export function sendStateEvent<T extends object>(
return mx.sendStateEvent(roomId, eventType as any, content, stateKey);
}
// Typed message send for the composer's fire-and-forget sends. The SDK's
// `sendMessage` wants its `RoomMessageEventContent` union, which the composer's
// extensible content (MSC3488 location, MSC3245 voice, `m.mentions`, encrypted
// `file`) doesn't narrow to; the single cast lives here. A failed send already
// shows on the local echo ("Failed to send" with Retry, or the consent prompt),
// so the rejection is swallowed rather than surfacing as an unhandled error.
export function sendRoomMessage(
mx: MatrixClient,
roomId: string,
threadId: string | null,
content: IContent,
): void {
mx.sendMessage(roomId, threadId, content as RoomMessageEventContent).catch(() => undefined);
}
// Typed send for events whose type isn't one of the SDK's typed timeline events
// (MSC3381 polls, forwarded arbitrary types, the fork's reaction enum) or whose
// content is extensible. Keeps the one cast out of every call site.
export function sendRoomEvent(
mx: MatrixClient,
roomId: string,
threadId: string | null,
eventType: string,
content: IContent,
): Promise<ISendEventResponse> {
return mx.sendEvent(
roomId,
threadId,
eventType as keyof TimelineEvents,
content as TimelineEvents[keyof TimelineEvents],
);
}
export const getAccountData = (
mx: MatrixClient,
eventType: AccountDataEvent | string,
): MatrixEvent | undefined => mx.getAccountData(eventType as any);
): MatrixEvent | undefined => mx.getAccountData(eventType as keyof AccountDataEvents);
export const getMDirects = (mDirectEvent: MatrixEvent): Set<string> => {
const roomIds = new Set<string>();