From cabe8ea5635301f53017e739f1ea718b8c7f15d0 Mon Sep 17 00:00:00 2001 From: Harshit2405-2004 Date: Sun, 5 Apr 2026 22:08:58 +0530 Subject: [PATCH] fix: replace 'any' types with proper TypeScript types (fixes #1265) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TYPE SAFETY FIX - Phase 1: Critical Paths Issue: https://github.com/RocketChat/EmbeddedChat/issues/1265 Problem: Widespread use of 'any' type across codebase (30+ occurrences) causing: - No IDE autocomplete or type checking - Runtime errors not caught at compile time - Refactoring breaks silently - Difficult to trace data flow - Production runtime errors Phase 1 Changes (Critical Paths): This commit addresses the most critical type safety issues in core authentication and API packages. New Type Definitions: - packages/api/src/types.ts - Message, User, ActionData, UiInteractionData - packages/auth/src/types.ts - CurrentUser, AuthToken Fixed Files: - packages/api/src/cloneArray.ts • Use generic type > • Properly typed cloning function - packages/api/src/EmbeddedChatApi.ts • onMessageCallbacks: Message type instead of any • onActionTriggeredCallbacks: ActionData instead of any • onUiInteractionCallbacks: UiInteractionData instead of any - packages/auth/src/RocketChatAuth.ts • currentUser: CurrentUser | null instead of any • authListeners: Properly typed callbacks - packages/auth/src/Api.ts • request() data parameter: unknown instead of any • ApiError constructor: unknown[] instead of any[] - packages/rc-app/lib/getCallbackContent.ts • config: CallbackConfig interface instead of any • error parameter: optional string type Exported Types: - packages/api/src/index.ts - Export all types - packages/auth/src/index.ts - Export all types Impact: ✅ Type safety in critical authentication paths ✅ IDE autocomplete for message callbacks ✅ Compile-time error detection ✅ Better refactoring support ✅ Clearer data flow Next Phase: - Remaining packages (24+ files) - Additional interfaces for complex types - Stricter tsconfig settings Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- packages/api/src/EmbeddedChatApi.ts | 7 +-- packages/api/src/cloneArray.ts | 10 ++-- packages/api/src/index.ts | 1 + packages/api/src/types.ts | 59 +++++++++++++++++++++++ packages/auth/src/Api.ts | 4 +- packages/auth/src/RocketChatAuth.ts | 6 ++- packages/auth/src/index.ts | 1 + packages/auth/src/types.ts | 23 +++++++++ packages/rc-app/lib/getCallbackContent.ts | 19 ++++++-- 9 files changed, 114 insertions(+), 16 deletions(-) create mode 100644 packages/api/src/types.ts create mode 100644 packages/auth/src/types.ts diff --git a/packages/api/src/EmbeddedChatApi.ts b/packages/api/src/EmbeddedChatApi.ts index 88eb4c23c2..af2136e969 100644 --- a/packages/api/src/EmbeddedChatApi.ts +++ b/packages/api/src/EmbeddedChatApi.ts @@ -6,6 +6,7 @@ import { RocketChatAuth, ApiError, } from "@embeddedchat/auth"; +import { Message, ActionData, UiInteractionData } from "./types"; // mutliple typing status can come at the same time they should be processed in order. let typingHandlerLock = 0; @@ -13,11 +14,11 @@ export default class EmbeddedChatApi { host: string; rid: string; rcClient: Rocketchat; - onMessageCallbacks: ((message: any) => void)[]; + onMessageCallbacks: ((message: Message) => void)[]; onMessageDeleteCallbacks: ((messageId: string) => void)[]; onTypingStatusCallbacks: ((users: string[]) => void)[]; - onActionTriggeredCallbacks: ((data: any) => void)[]; - onUiInteractionCallbacks: ((data: any) => void)[]; + onActionTriggeredCallbacks: ((data: ActionData) => void)[]; + onUiInteractionCallbacks: ((data: UiInteractionData) => void)[]; typingUsers: string[]; auth: RocketChatAuth; diff --git a/packages/api/src/cloneArray.ts b/packages/api/src/cloneArray.ts index ceb43ad438..fc990837fc 100644 --- a/packages/api/src/cloneArray.ts +++ b/packages/api/src/cloneArray.ts @@ -1,11 +1,11 @@ /** - * Deep Cloning upto 2 levels - * @param {*} array - * @returns + * Deep Cloning up to 2 levels + * @param array - Array to clone + * @returns Cloned array */ -const cloneArray = (array: any[]) => { +const cloneArray = >(array: T[]): T[] => { const newArray = [...array].map((item) => - typeof item === "object" ? { ...item } : item + typeof item === "object" && item !== null ? ({ ...item } as T) : item ); return newArray; }; diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index b4ff83a1c6..7a396c9bc8 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -1 +1,2 @@ export { default as EmbeddedChatApi } from "./EmbeddedChatApi"; +export * from "./types"; diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts new file mode 100644 index 0000000000..9e6533f464 --- /dev/null +++ b/packages/api/src/types.ts @@ -0,0 +1,59 @@ +/** + * Type definitions for EmbeddedChat API + * Issue #1265: Replace 'any' types with proper TypeScript types + */ + +export interface User { + _id: string; + username: string; + name?: string; + status?: string; + statusText?: string; + avatarETag?: string; +} + +export interface MessageUser { + _id: string; + username: string; + name?: string; +} + +export interface Attachment { + title?: string; + description?: string; + title_link?: string; + image_url?: string; + audio_url?: string; + video_url?: string; + type?: string; + [key: string]: unknown; +} + +export interface Message { + _id: string; + rid: string; + msg: string; + ts: Date | string; + u: MessageUser; + attachments?: Attachment[]; + mentions?: MessageUser[]; + channels?: string[]; + editedBy?: MessageUser; + editedAt?: Date | string; + [key: string]: unknown; // Allow additional fields from RocketChat +} + +export interface ActionData { + actionId: string; + value?: string; + blockId?: string; + appId?: string; + [key: string]: unknown; +} + +export interface UiInteractionData { + type: string; + triggerId?: string; + payload?: unknown; + [key: string]: unknown; +} diff --git a/packages/auth/src/Api.ts b/packages/auth/src/Api.ts index 78d6c82c7a..23b930de5b 100644 --- a/packages/auth/src/Api.ts +++ b/packages/auth/src/Api.ts @@ -4,7 +4,7 @@ export class ApiError extends Error { response: Response, message?: string | undefined, options?: ErrorOptions | undefined, - ...other: any[] + ...other: unknown[] ) { super(message, options, ...(other as [])); this.response = response; @@ -30,7 +30,7 @@ export class Api { async request( method: string = "GET", endpoint: string, - data: any, + data: unknown, config: RequestInit ) { const url = new URL(endpoint, this.baseUrl).toString(); diff --git a/packages/auth/src/RocketChatAuth.ts b/packages/auth/src/RocketChatAuth.ts index 0f2c55f196..bc28071c0b 100644 --- a/packages/auth/src/RocketChatAuth.ts +++ b/packages/auth/src/RocketChatAuth.ts @@ -5,12 +5,14 @@ import { IRocketChatAuthOptions } from "./IRocketChatAuthOptions"; import { Api, ApiError } from "./Api"; import loginWithRocketChatOAuth from "./loginWithRocketChatOAuth"; import handleSecureLogin from "./handleSecureLogin"; +import { CurrentUser } from "./types"; + class RocketChatAuth { host: string; api: Api; - currentUser: any; + currentUser: CurrentUser | null; lastFetched: Date; - authListeners: ((user: object | null) => void)[] = []; + authListeners: ((user: CurrentUser | null) => void)[] = []; deleteToken: () => Promise; saveToken: (token: string) => Promise; getToken: () => Promise; diff --git a/packages/auth/src/index.ts b/packages/auth/src/index.ts index ff36dfb6ab..8766429606 100644 --- a/packages/auth/src/index.ts +++ b/packages/auth/src/index.ts @@ -2,3 +2,4 @@ export * from "./auth"; export { default as RocketChatAuth } from "./RocketChatAuth"; export * from "./IRocketChatAuthOptions"; export { ApiError } from "./Api"; +export * from "./types"; diff --git a/packages/auth/src/types.ts b/packages/auth/src/types.ts new file mode 100644 index 0000000000..779a727a06 --- /dev/null +++ b/packages/auth/src/types.ts @@ -0,0 +1,23 @@ +/** + * Type definitions for RocketChat Authentication + * Issue #1265: Replace 'any' types with proper TypeScript types + */ + +export interface AuthToken { + authToken: string; + userId: string; +} + +export interface CurrentUser { + _id: string; + username: string; + name?: string; + status?: string; + statusConnection?: string; + utcOffset?: number; + active?: boolean; + roles?: string[]; + emails?: Array<{ address: string; verified: boolean }>; + authToken?: string; + [key: string]: unknown; // Allow additional fields +} diff --git a/packages/rc-app/lib/getCallbackContent.ts b/packages/rc-app/lib/getCallbackContent.ts index 165aa1c1bb..c70c3fc935 100644 --- a/packages/rc-app/lib/getCallbackContent.ts +++ b/packages/rc-app/lib/getCallbackContent.ts @@ -7,15 +7,26 @@ interface ICredentials { serviceName: string; } +interface CallbackConfig { + success: boolean; + error?: string; + origin?: string; + credentials?: { + accessToken: string; + expiresIn: number; + serviceName: string; + }; +} + export const getCallbackContent = async ( read: IRead, credentials: ICredentials | null, origin: string, - error + error?: string ) => { const { accessToken, expiresIn = 3600, serviceName } = credentials || {}; const isAllowed = await isAllowedOrigin(read, origin); - let config: any = {}; + let config: CallbackConfig; if (error) { config = { success: false, @@ -31,9 +42,9 @@ export const getCallbackContent = async ( success: true, origin, credentials: { - accessToken, + accessToken: accessToken!, expiresIn, - serviceName, + serviceName: serviceName!, }, }; }