fix: search for invitation in members table by rsbh · Pull Request #1457 · raystack/frontier

9-9: Type relaxation may break downstream type safety.

Changing MemberWithInvite to Partial<User> & Partial<Invitation> makes all fields optional, including id which is required for actual users. Downstream consumers (e.g., MembersActions accessing member.id for policy queries) lose compile-time guarantees that id exists for non-invitation members.

Consider a discriminated union to preserve type safety:

♻️ Suggested type definition
-export type MemberWithInvite = Partial<User> & Partial<Invitation> & { invited?: boolean };
+export type MemberWithInvite =
+  | (User & { invited?: false })
+  | (Invitation & { email: string; invited: true });

77-86: The email mapping is correct; consider removing the unsafe cast.

The logic to map userId to email for invitations correctly addresses the search issue. However, the as unknown as MemberWithInvite[] cast bypasses type checking entirely.

If the type definition is adjusted to a discriminated union (as suggested above), this cast becomes unnecessary and the code gains full type safety:

♻️ Suggested refactor
  const updatedUsers = useMemo(() => {
-   const invitations = (invitationsData?.invitations || []).map(user => {
-     return {
-       ...user,
-       email: user.userId,
-       invited: true
-     };
-   });
-   return [...users, ...invitations] as unknown as MemberWithInvite[];
+   const invitations: MemberWithInvite[] = (invitationsData?.invitations || []).map(inv => ({
+     ...inv,
+     email: inv.userId,
+     invited: true as const
+   }));
+   const userMembers: MemberWithInvite[] = users.map(u => ({ ...u, invited: false as const }));
+   return [...userMembers, ...invitations];
  }, [users, invitationsData?.invitations]);