diff --git a/playwright/e2e/crypto/crypto.spec.ts b/playwright/e2e/crypto/crypto.spec.ts
index d917a40839..3d7327b59d 100644
--- a/playwright/e2e/crypto/crypto.spec.ts
+++ b/playwright/e2e/crypto/crypto.spec.ts
@@ -87,7 +87,7 @@ const bobJoin = async (page: Page, bob: Bot) => {
 async function autoJoin(client: Client) {
     await client.evaluate((cli) => {
         cli.on(window.matrixcs.RoomMemberEvent.Membership, (event, member) => {
-            if (member.membership === "invite" && member.userId === cli.getUserId()) {
+            if (member.membership === Membership.Invite && member.userId === cli.getUserId()) {
                 cli.joinRoom(member.roomId);
             }
         });
diff --git a/playwright/e2e/crypto/verification.spec.ts b/playwright/e2e/crypto/verification.spec.ts
index 55d65a9b08..fdf01d8236 100644
--- a/playwright/e2e/crypto/verification.spec.ts
+++ b/playwright/e2e/crypto/verification.spec.ts
@@ -262,7 +262,7 @@ test.describe("User verification", () => {
         const bobVerificationRequest = await bob.evaluateHandle(
             async (client, { dmRoomId, aliceCredentials }) => {
                 const room = client.getRoom(dmRoomId);
-                while (room.getMember(aliceCredentials.userId)?.membership !== "join") {
+                while (room.getMember(aliceCredentials.userId)?.membership !== Membership.Join) {
                     await new Promise((resolve) => {
                         room.once(window.matrixcs.RoomStateEvent.Members, resolve);
                     });
diff --git a/playwright/e2e/knock/knock-into-room.spec.ts b/playwright/e2e/knock/knock-into-room.spec.ts
index 1a4974e891..4931db65cc 100644
--- a/playwright/e2e/knock/knock-into-room.spec.ts
+++ b/playwright/e2e/knock/knock-into-room.spec.ts
@@ -73,7 +73,7 @@ test.describe("Knock Into Room", () => {
             return events.some(
                 (e) =>
                     e.getType() === "m.room.member" &&
-                    e.getContent()?.membership === "knock" &&
+                    e.getContent()?.membership === Membership.Knock &&
                     e.getContent()?.displayname === "Alice",
             );
         });
@@ -108,7 +108,7 @@ test.describe("Knock Into Room", () => {
             return events.some(
                 (e) =>
                     e.getType() === "m.room.member" &&
-                    e.getContent()?.membership === "knock" &&
+                    e.getContent()?.membership === Membership.Knock &&
                     e.getContent()?.displayname === "Alice",
             );
         });
@@ -150,7 +150,7 @@ test.describe("Knock Into Room", () => {
             return events.some(
                 (e) =>
                     e.getType() === "m.room.member" &&
-                    e.getContent()?.membership === "knock" &&
+                    e.getContent()?.membership === Membership.Knock &&
                     e.getContent()?.displayname === "Alice",
             );
         });
@@ -192,7 +192,7 @@ test.describe("Knock Into Room", () => {
             return events.some(
                 (e) =>
                     e.getType() === "m.room.member" &&
-                    e.getContent()?.membership === "knock" &&
+                    e.getContent()?.membership === Membership.Knock &&
                     e.getContent()?.displayname === "Alice",
             );
         });
@@ -258,7 +258,7 @@ test.describe("Knock Into Room", () => {
             return events.some(
                 (e) =>
                     e.getType() === "m.room.member" &&
-                    e.getContent()?.membership === "knock" &&
+                    e.getContent()?.membership === Membership.Knock &&
                     e.getContent()?.displayname === "Alice",
             );
         });
diff --git a/playwright/e2e/knock/manage-knocks.spec.ts b/playwright/e2e/knock/manage-knocks.spec.ts
index 3fb5c68551..66f27bcbd5 100644
--- a/playwright/e2e/knock/manage-knocks.spec.ts
+++ b/playwright/e2e/knock/manage-knocks.spec.ts
@@ -73,7 +73,7 @@ test.describe("Manage Knocks", () => {
             return events.some(
                 (e) =>
                     e.getType() === "m.room.member" &&
-                    e.getContent()?.membership === "leave" &&
+                    e.getContent()?.membership === Membership.Leave &&
                     e.getContent()?.displayname === "Bob",
             );
         });
@@ -110,7 +110,7 @@ test.describe("Manage Knocks", () => {
             return events.some(
                 (e) =>
                     e.getType() === "m.room.member" &&
-                    e.getContent()?.membership === "leave" &&
+                    e.getContent()?.membership === Membership.Leave &&
                     e.getContent()?.displayname === "Bob",
             );
         });
diff --git a/playwright/pages/bot.ts b/playwright/pages/bot.ts
index d6b729420c..fc4ec75805 100644
--- a/playwright/pages/bot.ts
+++ b/playwright/pages/bot.ts
@@ -178,7 +178,7 @@ export class Bot extends Client {
 
                 if (opts.autoAcceptInvites) {
                     cli.on(window.matrixcs.RoomMemberEvent.Membership, (event, member) => {
-                        if (member.membership === "invite" && member.userId === cli.getUserId()) {
+                        if (member.membership === Membership.Invite && member.userId === cli.getUserId()) {
                             cli.joinRoom(member.roomId);
                         }
                     });
diff --git a/src/ScalarMessaging.ts b/src/ScalarMessaging.ts
index fb2801c9b6..86d48b678a 100644
--- a/src/ScalarMessaging.ts
+++ b/src/ScalarMessaging.ts
@@ -357,7 +357,7 @@ function inviteUser(event: MessageEvent<any>, roomId: string, userId: string): v
     if (room) {
         // if they are already invited or joined we can resolve immediately.
         const member = room.getMember(userId);
-        if (member && ["join", "invite"].includes(member.membership!)) {
+        if (member && [Membership.Join, Membership.Invite].includes(member.membership)) {
             sendResponse(event, {
                 success: true,
             });
@@ -669,7 +669,7 @@ function canSendEvent(event: MessageEvent<any>, roomId: string): void {
         sendError(event, _t("scalar|error_room_unknown"));
         return;
     }
-    if (room.getMyMembership() !== "join") {
+    if (room.getMyMembership() !== Membership.Join) {
         sendError(event, _t("scalar|error_membership"));
         return;
     }
diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx
index f309ca4bc5..a7a39538dc 100644
--- a/src/SlashCommands.tsx
+++ b/src/SlashCommands.tsx
@@ -241,7 +241,7 @@ export const Commands = [
             if (args) {
                 const ev = cli.getRoom(roomId)?.currentState.getStateEvents("m.room.member", cli.getSafeUserId());
                 const content = {
-                    ...(ev ? ev.getContent() : { membership: "join" }),
+                    ...(ev ? ev.getContent() : { membership: Membership.Join }),
                     displayname: args,
                 };
                 return success(cli.sendStateEvent(roomId, "m.room.member", content, cli.getSafeUserId()));
@@ -291,7 +291,7 @@ export const Commands = [
                     if (!url) return;
                     const ev = room?.currentState.getStateEvents("m.room.member", userId);
                     const content = {
-                        ...(ev ? ev.getContent() : { membership: "join" }),
+                        ...(ev ? ev.getContent() : { membership: Membership.Join }),
                         avatar_url: url,
                     };
                     return cli.sendStateEvent(roomId, "m.room.member", content, userId);
diff --git a/src/TextForEvent.tsx b/src/TextForEvent.tsx
index 3389e7d838..0729841d11 100644
--- a/src/TextForEvent.tsx
+++ b/src/TextForEvent.tsx
@@ -122,7 +122,7 @@ function textForMemberEvent(
     const reason = content.reason;
 
     switch (content.membership) {
-        case "invite": {
+        case Membership.Invite: {
             const threePidContent = content.third_party_invite;
             if (threePidContent) {
                 if (threePidContent.display_name) {
@@ -138,13 +138,13 @@ function textForMemberEvent(
                 return () => _t("timeline|m.room.member|invite", { senderName, targetName });
             }
         }
-        case "ban":
+        case Membership.Ban:
             return () =>
                 reason
                     ? _t("timeline|m.room.member|ban_reason", { senderName, targetName, reason })
                     : _t("timeline|m.room.member|ban", { senderName, targetName });
-        case "join":
-            if (prevContent && prevContent.membership === "join") {
+        case Membership.Join:
+            if (prevContent && prevContent.membership === Membership.Join) {
                 const modDisplayname = getModification(prevContent.displayname, content.displayname);
                 const modAvatarUrl = getModification(prevContent.avatar_url, content.avatar_url);
 
@@ -194,9 +194,9 @@ function textForMemberEvent(
                 if (!ev.target) logger.warn("Join message has no target! -- " + ev.getContent().state_key);
                 return () => _t("timeline|m.room.member|join", { targetName });
             }
-        case "leave":
+        case Membership.Leave:
             if (ev.getSender() === ev.getStateKey()) {
-                if (prevContent.membership === "invite") {
+                if (prevContent.membership === Membership.Invite) {
                     return () => _t("timeline|m.room.member|reject_invite", { targetName });
                 } else {
                     return () =>
@@ -204,9 +204,9 @@ function textForMemberEvent(
                             ? _t("timeline|m.room.member|left_reason", { targetName, reason })
                             : _t("timeline|m.room.member|left", { targetName });
                 }
-            } else if (prevContent.membership === "ban") {
+            } else if (prevContent.membership === Membership.Ban) {
                 return () => _t("timeline|m.room.member|unban", { senderName, targetName });
-            } else if (prevContent.membership === "invite") {
+            } else if (prevContent.membership === Membership.Invite) {
                 return () =>
                     reason
                         ? _t("timeline|m.room.member|withdrew_invite_reason", {
@@ -215,7 +215,7 @@ function textForMemberEvent(
                               reason,
                           })
                         : _t("timeline|m.room.member|withdrew_invite", { senderName, targetName });
-            } else if (prevContent.membership === "join") {
+            } else if (prevContent.membership === Membership.Join) {
                 return () =>
                     reason
                         ? _t("timeline|m.room.member|kick_reason", {
diff --git a/src/VoipUserMapper.ts b/src/VoipUserMapper.ts
index d03a38b333..ff29f5a6bd 100644
--- a/src/VoipUserMapper.ts
+++ b/src/VoipUserMapper.ts
@@ -95,7 +95,7 @@ export default class VoipUserMapper {
         if (!virtualRoomEvent || !virtualRoomEvent.getContent()) return null;
         const nativeRoomID = virtualRoomEvent.getContent()["native_room"];
         const nativeRoom = cli.getRoom(nativeRoomID);
-        if (!nativeRoom || nativeRoom.getMyMembership() !== "join") return null;
+        if (!nativeRoom || nativeRoom.getMyMembership() !== Membership.Join) return null;
 
         return nativeRoomID;
     }
diff --git a/src/autocomplete/UserProvider.tsx b/src/autocomplete/UserProvider.tsx
index 728f4fcec2..e84579ad7d 100644
--- a/src/autocomplete/UserProvider.tsx
+++ b/src/autocomplete/UserProvider.tsx
@@ -159,7 +159,7 @@ export default class UserProvider extends AutocompleteProvider {
 
         const currentUserId = MatrixClientPeg.safeGet().credentials.userId;
         this.users = this.room.getJoinedMembers().filter(({ userId }) => userId !== currentUserId);
-        this.users = this.users.concat(this.room.getMembersWithMembership("invite"));
+        this.users = this.users.concat(this.room.getMembersWithMembership(Membership.Invite));
 
         this.users = sortBy(this.users, (member) => 1e20 - lastSpoken[member.userId] || 1e20);
 
diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx
index db4a5e752f..2ca917a06b 100644
--- a/src/components/structures/RoomView.tsx
+++ b/src/components/structures/RoomView.tsx
@@ -1454,7 +1454,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
     private async loadMembersIfJoined(room: Room): Promise<void> {
         // lazy load members if enabled
         if (this.context.client?.hasLazyLoadMembersEnabled()) {
-            if (room && room.getMyMembership() === "join") {
+            if (room && room.getMyMembership() === Membership.Join) {
                 try {
                     await room.loadMembersIfNeeded();
                     if (!this.unmounted) {
@@ -1588,7 +1588,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
         if (room && this.context.client) {
             const me = this.context.client.getSafeUserId();
             const canReact =
-                room.getMyMembership() === "join" && room.currentState.maySendEvent(EventType.Reaction, me);
+                room.getMyMembership() === Membership.Join && room.currentState.maySendEvent(EventType.Reaction, me);
             const canSendMessages = room.maySendMessage();
             const canSelfRedact = room.currentState.maySendEvent(EventType.RoomRedaction, me);
 
@@ -1622,7 +1622,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 
     private updateDMState(): void {
         const room = this.state.room;
-        if (room?.getMyMembership() !== "join") {
+        if (room.getMyMembership() != Membership.Join) {
             return;
         }
         const dmInviter = room.getDMInviter();
@@ -1662,7 +1662,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
                         action: Action.JoinRoom,
                         roomId,
                         opts: { inviteSignUrl: signUrl },
-                        metricsTrigger: this.state.room?.getMyMembership() === "invite" ? "Invite" : "RoomPreview",
+                        metricsTrigger:
+                            this.state.room?.getMyMembership() === Membership.Invite ? "Invite" : "RoomPreview",
                         canAskToJoin: this.state.canAskToJoin,
                     });
                 }
@@ -2183,7 +2184,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
         const myMembership = this.state.room.getMyMembership();
         if (
             isVideoRoom(this.state.room) &&
-            !(SettingsStore.getValue("feature_video_rooms") && myMembership === "join")
+            !(SettingsStore.getValue("feature_video_rooms") && myMembership === Membership.Join)
         ) {
             return (
                 <ErrorBoundary>
@@ -2200,7 +2201,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
         }
 
         // SpaceRoomView handles invites itself
-        if (myMembership === "invite" && !this.state.room.isSpaceRoom()) {
+        if (myMembership === Membership.Invite && !this.state.room.isSpaceRoom()) {
             if (this.state.joining || this.state.rejecting) {
                 return (
                     <ErrorBoundary>
@@ -2247,16 +2248,16 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
             }
         }
 
-        if (this.state.canAskToJoin && ["knock", "leave"].includes(myMembership)) {
+        if (this.state.canAskToJoin && [Membership.Knock, Membership.Leave].includes(myMembership)) {
             return (
                 <div className="mx_RoomView" data-room-header={roomHeaderType}>
                     <ErrorBoundary>
                         <RoomPreviewBar
                             onJoinClick={this.onJoinButtonClicked}
                             room={this.state.room}
-                            canAskToJoinAndMembershipIsLeave={myMembership === "leave"}
+                            canAskToJoinAndMembershipIsLeave={myMembership === Membership.Leave}
                             promptAskToJoin={this.state.promptAskToJoin}
-                            knocked={myMembership === "knock"}
+                            knocked={myMembership === Membership.Knock}
                             onSubmitAskToJoin={this.onSubmitAskToJoin}
                             onCancelAskToJoin={this.onCancelAskToJoin}
                             onForgetClick={this.onForgetClick}
@@ -2288,7 +2289,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
             statusBar = (
                 <RoomStatusBar
                     room={this.state.room}
-                    isPeeking={myMembership !== "join"}
+                    isPeeking={myMembership !== Membership.Join}
                     onInviteClick={this.onInviteClick}
                     onVisible={this.onStatusBarVisible}
                     onHidden={this.onStatusBarHidden}
@@ -2332,7 +2333,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
             );
         } else if (showRoomUpgradeBar) {
             aux = <RoomUpgradeWarningBar room={this.state.room} />;
-        } else if (myMembership !== "join") {
+        } else if (myMembership !== Membership.Join) {
             // We do have a room object for this room, but we're not currently in it.
             // We may have a 3rd party invite to it.
             let inviterName: string | undefined;
@@ -2406,7 +2407,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
         let messageComposer;
         const showComposer =
             // joined and not showing search results
-            myMembership === "join" && !this.state.search;
+            myMembership === Membership.Join && !this.state.search;
         if (showComposer) {
             messageComposer = (
                 <MessageComposer
@@ -2603,7 +2604,8 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
 
         const myMember = this.state.room!.getMember(this.context.client!.getSafeUserId());
         const showForgetButton =
-            !this.context.client.isGuest() && (["leave", "ban"].includes(myMembership) || myMember?.isKicked());
+            !this.context.client.isGuest() &&
+            ([Membership.Leave, Membership.Ban].includes(myMembership) || myMember?.isKicked());
 
         return (
             <RoomContext.Provider value={this.state}>
@@ -2640,7 +2642,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
                                         room={this.state.room}
                                         searchInfo={this.state.search}
                                         oobData={this.props.oobData}
-                                        inRoom={myMembership === "join"}
+                                        inRoom={myMembership === Membership.Join}
                                         onSearchClick={onSearchClick}
                                         onInviteClick={onInviteClick}
                                         onForgetClick={showForgetButton ? onForgetClick : null}
diff --git a/src/components/structures/SpaceHierarchy.tsx b/src/components/structures/SpaceHierarchy.tsx
index feeacb4581..8927628020 100644
--- a/src/components/structures/SpaceHierarchy.tsx
+++ b/src/components/structures/SpaceHierarchy.tsx
@@ -112,7 +112,7 @@ const Tile: React.FC<ITileProps> = ({
     const cli = useContext(MatrixClientContext);
     const joinedRoom = useTypedEventEmitterState(cli, ClientEvent.Room, () => {
         const cliRoom = cli?.getRoom(room.room_id);
-        return cliRoom?.getMyMembership() === "join" ? cliRoom : undefined;
+        return cliRoom?.getMyMembership() === Membership.Join ? cliRoom : undefined;
     });
     const joinedRoomName = useTypedEventEmitterState(joinedRoom, RoomEvent.Name, (room) => room?.name);
     const name =
@@ -828,7 +828,7 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
                     content = <Spinner />;
                 } else {
                     const hasPermissions =
-                        space?.getMyMembership() === "join" &&
+                        space?.getMyMembership() === Membership.Join &&
                         space.currentState.maySendStateEvent(EventType.SpaceChild, cli.getSafeUserId());
 
                     const root = hierarchy.roomMap.get(space.roomId);
@@ -846,7 +846,7 @@ const SpaceHierarchy: React.FC<IProps> = ({ space, initialText = "", showRoom, a
                                     onViewRoomClick={(roomId, roomType) => showRoom(cli, hierarchy, roomId, roomType)}
                                     onJoinRoomClick={async (roomId, parents) => {
                                         for (const parent of parents) {
-                                            if (cli.getRoom(parent)?.getMyMembership() !== "join") {
+                                            if (cli.getRoom(parent)?.getMyMembership() !== Membership.Join) {
                                                 await joinRoom(cli, hierarchy, parent);
                                             }
                                         }
diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx
index dc79a25489..451a91ead7 100644
--- a/src/components/structures/SpaceRoomView.tsx
+++ b/src/components/structures/SpaceRoomView.tsx
@@ -237,7 +237,7 @@ const SpaceLanding: React.FC<{ space: Room }> = ({ space }) => {
     }
 
     const hasAddRoomPermissions =
-        myMembership === "join" && space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
+        myMembership === Membership.Join && space.currentState.maySendStateEvent(EventType.SpaceChild, userId);
 
     let addRoomButton;
     if (hasAddRoomPermissions) {
@@ -678,7 +678,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
     private renderBody(): JSX.Element {
         switch (this.state.phase) {
             case Phase.Landing:
-                if (this.state.myMembership === "join") {
+                if (this.state.myMembership === Membership.Join) {
                     return <SpaceLanding space={this.props.space} />;
                 } else {
                     return (
diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx
index 7d1ce90762..ad5da6cc47 100644
--- a/src/components/structures/TimelinePanel.tsx
+++ b/src/components/structures/TimelinePanel.tsx
@@ -1824,7 +1824,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
         // that the event belongs to, and traversing the timeline looking for
         // that event, while keeping track of the user's membership
         let i = events.length - 1;
-        let userMembership = "leave";
+        let userMembership = Membership.Leave;
         for (; i >= 0; i--) {
             const timeline = this.props.timelineSet.getTimelineForEvent(events[i].getId()!);
             if (!timeline) {
@@ -1837,14 +1837,15 @@ class TimelinePanel extends React.Component<IProps, IState> {
                 continue;
             }
 
-            userMembership = timeline.getState(EventTimeline.FORWARDS)?.getMember(userId)?.membership ?? "leave";
+            userMembership =
+                timeline.getState(EventTimeline.FORWARDS)?.getMember(userId)?.membership ?? Membership.Leave;
             const timelineEvents = timeline.getEvents();
             for (let j = timelineEvents.length - 1; j >= 0; j--) {
                 const event = timelineEvents[j];
                 if (event.getId() === events[i].getId()) {
                     break;
                 } else if (event.getStateKey() === userId && event.getType() === EventType.RoomMember) {
-                    userMembership = event.getPrevContent().membership || "leave";
+                    userMembership = event.getPrevContent().membership || Membership.Leave;
                 }
             }
             break;
@@ -1855,8 +1856,11 @@ class TimelinePanel extends React.Component<IProps, IState> {
         for (; i >= 0; i--) {
             const event = events[i];
             if (event.getStateKey() === userId && event.getType() === EventType.RoomMember) {
-                userMembership = event.getPrevContent().membership || "leave";
-            } else if (userMembership === "leave" && (event.isDecryptionFailure() || event.isBeingDecrypted())) {
+                userMembership = event.getPrevContent().membership || Membership.Leave;
+            } else if (
+                userMembership === Membership.Leave &&
+                (event.isDecryptionFailure() || event.isBeingDecrypted())
+            ) {
                 // reached an undecryptable message when the user wasn't in the room -- don't try to load any more
                 // Note: for now, we assume that events that are being decrypted are
                 // not decryptable - we will be called once more when it is decrypted.
diff --git a/src/components/structures/grouper/CreationGrouper.tsx b/src/components/structures/grouper/CreationGrouper.tsx
index 0ceb6f5857..375bfd76bc 100644
--- a/src/components/structures/grouper/CreationGrouper.tsx
+++ b/src/components/structures/grouper/CreationGrouper.tsx
@@ -48,7 +48,7 @@ export class CreationGrouper extends BaseGrouper {
         const eventType = event.getType();
         if (
             eventType === EventType.RoomMember &&
-            (event.getStateKey() !== createEvent.getSender() || event.getContent()["membership"] !== "join")
+            (event.getStateKey() !== createEvent.getSender() || event.getContent()["membership"] !== Membership.Join)
         ) {
             return false;
         }
diff --git a/src/components/views/context_menus/RoomContextMenu.tsx b/src/components/views/context_menus/RoomContextMenu.tsx
index 7527ef4fee..5c2d385cd1 100644
--- a/src/components/views/context_menus/RoomContextMenu.tsx
+++ b/src/components/views/context_menus/RoomContextMenu.tsx
@@ -144,7 +144,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
     let favouriteOption: JSX.Element | undefined;
     let lowPriorityOption: JSX.Element | undefined;
     let notificationOption: JSX.Element | undefined;
-    if (room.getMyMembership() === "join") {
+    if (room.getMyMembership() === Membership.Join) {
         const isFavorite = roomTags.includes(DefaultTagID.Favourite);
         favouriteOption = (
             <IconizedContextMenuCheckbox
diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx
index 39a00f1034..26e61b6333 100644
--- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx
+++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx
@@ -140,7 +140,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
     const msc3946ProcessDynamicPredecessor = useSettingValue<boolean>("feature_dynamic_room_predecessors");
     const visibleRooms = useMemo(
         () =>
-            cli?.getVisibleRooms(msc3946ProcessDynamicPredecessor).filter((r) => r.getMyMembership() === "join") ?? [],
+            cli?.getVisibleRooms(msc3946ProcessDynamicPredecessor).filter((r) => r.getMyMembership() === Membership.Join) ?? [],
         [cli, msc3946ProcessDynamicPredecessor],
     );
 
diff --git a/src/components/views/dialogs/ForwardDialog.tsx b/src/components/views/dialogs/ForwardDialog.tsx
index fe73738629..accc890f28 100644
--- a/src/components/views/dialogs/ForwardDialog.tsx
+++ b/src/components/views/dialogs/ForwardDialog.tsx
@@ -267,7 +267,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
             sortRooms(
                 cli
                     .getVisibleRooms(msc3946DynamicRoomPredecessors)
-                    .filter((room) => room.getMyMembership() === "join" && !room.isSpaceRoom()),
+                    .filter((room) => room.getMyMembership() === Membership.Join && !room.isSpaceRoom()),
             ),
         [cli, msc3946DynamicRoomPredecessors],
     );
diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx
index 08dbddc21d..2301940feb 100644
--- a/src/components/views/dialogs/InviteDialog.tsx
+++ b/src/components/views/dialogs/InviteDialog.tsx
@@ -372,10 +372,10 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial
             const room = MatrixClientPeg.safeGet().getRoom(props.roomId);
             const isFederated = room?.currentState.getStateEvents(EventType.RoomCreate, "")?.getContent()["m.federate"];
             if (!room) throw new Error("Room ID given to InviteDialog does not look like a room");
-            room.getMembersWithMembership("invite").forEach((m) => excludedIds.add(m.userId));
-            room.getMembersWithMembership("join").forEach((m) => excludedIds.add(m.userId));
+            room.getMembersWithMembership(Membership.Invite).forEach((m) => excludedIds.add(m.userId));
+            room.getMembersWithMembership(Membership.Join).forEach((m) => excludedIds.add(m.userId));
             // add banned users, so we don't try to invite them
-            room.getMembersWithMembership("ban").forEach((m) => excludedIds.add(m.userId));
+            room.getMembersWithMembership(Membership.Ban).forEach((m) => excludedIds.add(m.userId));
             if (isFederated === false) {
                 // exclude users from external servers
                 const homeserver = props.roomId.split(":")[1];
diff --git a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx
index 0799ff88f1..3dc353dee0 100644
--- a/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx
+++ b/src/components/views/dialogs/ManageRestrictedJoinRuleDialog.tsx
@@ -102,7 +102,7 @@ const ManageRestrictedJoinRuleDialog: React.FC<IProps> = ({ room, selected = [],
                     if (!room) {
                         return { roomId, name: roomId } as Room;
                     }
-                    if (room.getMyMembership() !== "join" || !room.isSpaceRoom()) {
+                    if (room.getMyMembership() !== Membership.Join || !room.isSpaceRoom()) {
                         return room;
                     }
                 }),
diff --git a/src/components/views/dialogs/devtools/ServersInRoom.tsx b/src/components/views/dialogs/devtools/ServersInRoom.tsx
index 4096a22626..6fff5fd511 100644
--- a/src/components/views/dialogs/devtools/ServersInRoom.tsx
+++ b/src/components/views/dialogs/devtools/ServersInRoom.tsx
@@ -27,7 +27,7 @@ const ServersInRoom: React.FC<IDevtoolsProps> = ({ onBack }) => {
     const servers = useMemo<Record<string, number>>(() => {
         const servers: Record<string, number> = {};
         context.room.currentState.getStateEvents(EventType.RoomMember).forEach((ev) => {
-            if (ev.getContent().membership !== "join") return; // only count joined users
+            if (ev.getContent().membership !== Membership.Join) return; // only count joined users
             const server = ev.getSender()!.split(":")[1];
             servers[server] = (servers[server] ?? 0) + 1;
         });
diff --git a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx
index 04df85e79f..575bdbf3a9 100644
--- a/src/components/views/dialogs/spotlight/SpotlightDialog.tsx
+++ b/src/components/views/dialogs/spotlight/SpotlightDialog.tsx
@@ -244,7 +244,7 @@ const findVisibleRooms = (cli: MatrixClient, msc3946ProcessDynamicPredecessor: b
         if (isLocalRoom(room)) return false;
 
         // TODO we may want to put invites in their own list
-        return room.getMyMembership() === "join" || room.getMyMembership() == "invite";
+        return room.getMyMembership() === Membership.Join || room.getMyMembership() == Membership.Invite;
     });
 };
 
@@ -675,7 +675,7 @@ const SpotlightDialog: React.FC<IProps> = ({ initialText = "", initialFilter = n
                 // world readable, a modal will appear asking you to register first. If
                 // it is readable, the preview appears as normal.
                 const showViewButton =
-                    clientRoom?.getMyMembership() === "join" ||
+                    clientRoom?.getMyMembership() === Membership.Join ||
                     (result.publicRoom.world_readable && !canAskToJoin(joinRule)) ||
                     cli.isGuest();
 
diff --git a/src/components/views/elements/AppTile.tsx b/src/components/views/elements/AppTile.tsx
index f55751420e..04cbbdd3e9 100644
--- a/src/components/views/elements/AppTile.tsx
+++ b/src/components/views/elements/AppTile.tsx
@@ -219,7 +219,10 @@ export default class AppTile extends React.Component<IProps, IState> {
     }
 
     private onMyMembership = (room: Room, membership: string): void => {
-        if ((membership === "leave" || membership === "ban") && room.roomId === this.props.room?.roomId) {
+        if (
+            (membership === Membership.Leave || membership === Membership.Ban) &&
+            room.roomId === this.props.room?.roomId
+        ) {
             this.onUserLeftRoom();
         }
     };
diff --git a/src/components/views/elements/EventListSummary.tsx b/src/components/views/elements/EventListSummary.tsx
index 4d9bbe35c9..8fcb538b80 100644
--- a/src/components/views/elements/EventListSummary.tsx
+++ b/src/components/views/elements/EventListSummary.tsx
@@ -416,12 +416,12 @@ export default class EventListSummary extends React.Component<
 
             case EventType.RoomMember:
                 switch (e.mxEvent.getContent().membership) {
-                    case "invite":
+                    case Membership.Invite:
                         return TransitionType.Invited;
-                    case "ban":
+                    case Membership.Ban:
                         return TransitionType.Banned;
-                    case "join":
-                        if (e.mxEvent.getPrevContent().membership === "join") {
+                    case Membership.Join:
+                        if (e.mxEvent.getPrevContent().membership === Membership.Join) {
                             if (e.mxEvent.getContent().displayname !== e.mxEvent.getPrevContent().displayname) {
                                 return TransitionType.ChangedName;
                             } else if (e.mxEvent.getContent().avatar_url !== e.mxEvent.getPrevContent().avatar_url) {
@@ -431,17 +431,17 @@ export default class EventListSummary extends React.Component<
                         } else {
                             return TransitionType.Joined;
                         }
-                    case "leave":
+                    case Membership.Leave:
                         if (e.mxEvent.getSender() === e.mxEvent.getStateKey()) {
-                            if (e.mxEvent.getPrevContent().membership === "invite") {
+                            if (e.mxEvent.getPrevContent().membership === Membership.Invite) {
                                 return TransitionType.InviteReject;
                             }
                             return TransitionType.Left;
                         }
                         switch (e.mxEvent.getPrevContent().membership) {
-                            case "invite":
+                            case Membership.Invite:
                                 return TransitionType.InviteWithdrawal;
-                            case "ban":
+                            case Membership.Ban:
                                 return TransitionType.Unbanned;
                             // sender is not target and made the target leave, if not from invite/ban then this is a kick
                             default:
diff --git a/src/components/views/elements/RoomFacePile.tsx b/src/components/views/elements/RoomFacePile.tsx
index 6ec333c4e5..46490f870f 100644
--- a/src/components/views/elements/RoomFacePile.tsx
+++ b/src/components/views/elements/RoomFacePile.tsx
@@ -38,7 +38,7 @@ interface IProps extends HTMLAttributes<HTMLSpanElement> {
 
 const RoomFacePile: FC<IProps> = ({ room, onlyKnownUsers = true, numShown = DEFAULT_NUM_FACES, ...props }) => {
     const cli = useContext(MatrixClientContext);
-    const isJoined = room.getMyMembership() === "join";
+    const isJoined = room.getMyMembership() === Membership.Join;
     let members = useRoomMembers(room);
     const count = members.length;
 
diff --git a/src/components/views/right_panel/TimelineCard.tsx b/src/components/views/right_panel/TimelineCard.tsx
index 7e3ce09e50..094b1675f7 100644
--- a/src/components/views/right_panel/TimelineCard.tsx
+++ b/src/components/views/right_panel/TimelineCard.tsx
@@ -217,7 +217,7 @@ export default class TimelineCard extends React.Component<IProps, IState> {
         const isUploading = ContentMessages.sharedInstance().getCurrentUploads(this.props.composerRelation).length > 0;
 
         const myMembership = this.props.room.getMyMembership();
-        const showComposer = myMembership === "join";
+        const showComposer = myMembership === Membership.Join;
 
         return (
             <RoomContext.Provider
diff --git a/src/components/views/right_panel/UserInfo.tsx b/src/components/views/right_panel/UserInfo.tsx
index a339a1ebce..f89a4f4484 100644
--- a/src/components/views/right_panel/UserInfo.tsx
+++ b/src/components/views/right_panel/UserInfo.tsx
@@ -473,7 +473,7 @@ export const UserOptionsSection: React.FC<{
         if (
             member instanceof RoomMember &&
             canInvite &&
-            (member?.membership ?? "leave") === "leave" &&
+            (member?.membership ?? Membership.Leave) === Membership.Leave &&
             shouldShowComponent(UIComponent.InviteUsers)
         ) {
             const roomId = member && member.roomId ? member.roomId : SdkContextClass.instance.roomViewStore.getRoomId();
@@ -638,7 +638,7 @@ export const RoomKickButton = ({
     const cli = useContext(MatrixClientContext);
 
     // check if user can be kicked/disinvited
-    if (member.membership !== "invite" && member.membership !== "join") return <></>;
+    if (member.membership !== Membership.Invite && member.membership !== Membership.Join) return <></>;
 
     const onKick = async (): Promise<void> => {
         if (isUpdating) return; // only allow one operation at a time
@@ -647,17 +647,17 @@ export const RoomKickButton = ({
         const commonProps = {
             member,
             action: room.isSpaceRoom()
-                ? member.membership === "invite"
+                ? member.membership === Membership.Invite
                     ? _t("user_info|disinvite_button_space")
                     : _t("user_info|kick_button_space")
-                : member.membership === "invite"
+                : member.membership === Membership.Invite
                   ? _t("user_info|disinvite_button_room")
                   : _t("user_info|kick_button_room"),
             title:
-                member.membership === "invite"
+                member.membership === Membership.Invite
                     ? _t("user_info|disinvite_button_room_name", { roomName: room.name })
                     : _t("user_info|kick_button_room_name", { roomName: room.name }),
-            askReason: member.membership === "join",
+            askReason: member.membership === Membership.Join,
             danger: true,
         };
 
@@ -718,10 +718,10 @@ export const RoomKickButton = ({
     };
 
     const kickLabel = room.isSpaceRoom()
-        ? member.membership === "invite"
+        ? member.membership === Membership.Invite
             ? _t("user_info|disinvite_button_space")
             : _t("user_info|kick_button_space")
-        : member.membership === "invite"
+        : member.membership === Membership.Invite
           ? _t("user_info|disinvite_button_room")
           : _t("user_info|kick_button_room");
 
@@ -771,7 +771,7 @@ export const BanToggleButton = ({
 }: Omit<IBaseRoomProps, "powerLevels">): JSX.Element => {
     const cli = useContext(MatrixClientContext);
 
-    const isBanned = member.membership === "ban";
+    const isBanned = member.membership === Membership.Ban;
     const onBanOrUnban = async (): Promise<void> => {
         if (isUpdating) return; // only allow one operation at a time
         startUpdating();
@@ -808,9 +808,9 @@ export const BanToggleButton = ({
                               return (
                                   !!myMember &&
                                   !!theirMember &&
-                                  theirMember.membership === "ban" &&
+                                  theirMember.membership === Membership.Ban &&
                                   myMember.powerLevel > theirMember.powerLevel &&
-                                  child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel)
+                                  child.currentState.hasSufficientPowerLevelFor(Membership.Ban, myMember.powerLevel)
                               );
                           }
                         : (child: Room) => {
@@ -820,9 +820,9 @@ export const BanToggleButton = ({
                               return (
                                   !!myMember &&
                                   !!theirMember &&
-                                  theirMember.membership !== "ban" &&
+                                  theirMember.membership !== Membership.Ban &&
                                   myMember.powerLevel > theirMember.powerLevel &&
-                                  child.currentState.hasSufficientPowerLevelFor("ban", myMember.powerLevel)
+                                  child.currentState.hasSufficientPowerLevelFor(Membership.Ban, myMember.powerLevel)
                               );
                           },
                     allLabel: isBanned ? _t("user_info|unban_space_everything") : _t("user_info|ban_space_everything"),
@@ -903,7 +903,7 @@ const MuteToggleButton: React.FC<IBaseRoomProps> = ({
     const cli = useContext(MatrixClientContext);
 
     // Don't show the mute/unmute option if the user is not in the room
-    if (member.membership !== "join") return null;
+    if (member.membership !== Membership.Join) return null;
 
     const muted = isMuted(member, powerLevels);
     const onMuteToggle = async (): Promise<void> => {
diff --git a/src/components/views/rooms/MemberList.tsx b/src/components/views/rooms/MemberList.tsx
index 639e1493b2..1ecd36c2ce 100644
--- a/src/components/views/rooms/MemberList.tsx
+++ b/src/components/views/rooms/MemberList.tsx
@@ -171,7 +171,7 @@ export default class MemberList extends React.Component<IProps, IState> {
     };
 
     private onMyMembership = (room: Room, membership: string, oldMembership?: string): void => {
-        if (room.roomId === this.props.roomId && membership === "join" && oldMembership !== "join") {
+        if (room.roomId === this.props.roomId && membership === Membership.Join && oldMembership !== Membership.Join) {
             // we just joined the room, load the member list
             this.updateListNow(true);
         }
@@ -363,7 +363,7 @@ export default class MemberList extends React.Component<IProps, IState> {
         const room = cli.getRoom(this.props.roomId);
         let inviteButton: JSX.Element | undefined;
 
-        if (room?.getMyMembership() === "join" && shouldShowComponent(UIComponent.InviteUsers)) {
+        if (room?.getMyMembership() === Membership.Join && shouldShowComponent(UIComponent.InviteUsers)) {
             const inviteButtonText = room.isSpaceRoom() ? _t("space|invite_this_space") : _t("room|invite_this_room");
 
             const button = (
diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx
index 4cc9028d48..c02c6191b4 100644
--- a/src/components/views/rooms/NewRoomIntro.tsx
+++ b/src/components/views/rooms/NewRoomIntro.tsx
@@ -112,7 +112,7 @@ const NewRoomIntro: React.FC = () => {
             </React.Fragment>
         );
     } else {
-        const inRoom = room && room.getMyMembership() === "join";
+        const inRoom = room && room.getMyMembership() === Membership.Join;
         const topic = room.currentState.getStateEvents(EventType.RoomTopic, "")?.getContent()?.topic;
         const canAddTopic = inRoom && room.currentState.maySendStateEvent(EventType.RoomTopic, cli.getSafeUserId());
 
diff --git a/src/components/views/rooms/RoomInfoLine.tsx b/src/components/views/rooms/RoomInfoLine.tsx
index cca5e2adaf..a4d88a6d0b 100644
--- a/src/components/views/rooms/RoomInfoLine.tsx
+++ b/src/components/views/rooms/RoomInfoLine.tsx
@@ -33,7 +33,7 @@ interface IProps {
 const RoomInfoLine: FC<IProps> = ({ room }) => {
     // summary will begin as undefined whilst loading and go null if it fails to load or we are not invited.
     const summary = useAsyncMemo(async (): Promise<Awaited<ReturnType<MatrixClient["getRoomSummary"]>> | null> => {
-        if (room.getMyMembership() !== "invite") return null;
+        if (room.getMyMembership() !== Membership.Invite) return null;
         try {
             return await room.client.getRoomSummary(room.roomId);
         } catch (e) {
@@ -61,7 +61,7 @@ const RoomInfoLine: FC<IProps> = ({ room }) => {
     }
 
     let members: JSX.Element | undefined;
-    if (membership === "invite" && summary) {
+    if (membership === Membership.Invite && summary) {
         // Don't trust local state and instead use the summary API
         members = (
             <span className="mx_RoomInfoLine_members">
diff --git a/src/components/views/rooms/RoomKnocksBar.tsx b/src/components/views/rooms/RoomKnocksBar.tsx
index edac6d47a8..0ecbbe47ee 100644
--- a/src/components/views/rooms/RoomKnocksBar.tsx
+++ b/src/components/views/rooms/RoomKnocksBar.tsx
@@ -35,7 +35,7 @@ export const RoomKnocksBar: VFC<{ room: Room }> = ({ room }) => {
     const knockMembers = useTypedEventEmitterState(
         room,
         RoomStateEvent.Update,
-        useCallback(() => room.getMembersWithMembership("knock"), [room]),
+        useCallback(() => room.getMembersWithMembership(Membership.Knock), [room]),
     );
     const knockMembersCount = knockMembers.length;
 
diff --git a/src/components/views/rooms/RoomPreviewBar.tsx b/src/components/views/rooms/RoomPreviewBar.tsx
index e3b06e5b9f..845106c5b5 100644
--- a/src/components/views/rooms/RoomPreviewBar.tsx
+++ b/src/components/views/rooms/RoomPreviewBar.tsx
@@ -192,13 +192,13 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
         if (myMember) {
             const previousMembership = myMember.events.member?.getPrevContent().membership;
             if (myMember.isKicked()) {
-                if (previousMembership === "knock") {
+                if (previousMembership === Membership.Knock) {
                     return MessageCase.RequestDenied;
                 } else if (this.props.promptAskToJoin) {
                     return MessageCase.PromptAskToJoin;
                 }
                 return MessageCase.Kicked;
-            } else if (myMember.membership === "ban") {
+            } else if (myMember.membership === Membership.Ban) {
                 return MessageCase.Banned;
             }
         }
@@ -284,7 +284,7 @@ export default class RoomPreviewBar extends React.Component<IProps, IState> {
             return false;
         }
         const memberContent = myMember.events.member?.getContent();
-        return memberContent?.membership === "invite" && memberContent.is_direct;
+        return memberContent?.membership === Membership.Invite && memberContent.is_direct;
     }
 
     private makeScreenAfterLogin(): { screen: string; params: Record<string, any> } {
diff --git a/src/components/views/rooms/RoomPreviewCard.tsx b/src/components/views/rooms/RoomPreviewCard.tsx
index 91893847d0..1bdb5baa1e 100644
--- a/src/components/views/rooms/RoomPreviewCard.tsx
+++ b/src/components/views/rooms/RoomPreviewCard.tsx
@@ -73,7 +73,7 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
 
     let inviterSection: JSX.Element | null = null;
     let joinButtons: JSX.Element;
-    if (myMembership === "join") {
+    if (myMembership === Membership.Join) {
         joinButtons = (
             <AccessibleButton
                 kind="danger_outline"
@@ -87,7 +87,7 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
                 {_t("action|leave")}
             </AccessibleButton>
         );
-    } else if (myMembership === "invite") {
+    } else if (myMembership === Membership.Invite) {
         const inviteSender = room.getMember(cli.getUserId()!)?.events.member?.getSender();
 
         if (inviteSender) {
@@ -178,7 +178,7 @@ const RoomPreviewCard: FC<IProps> = ({ room, onJoinButtonClicked, onRejectButton
         });
     } else if (isVideoRoom && !videoRoomsEnabled) {
         notice =
-            myMembership === "join"
+            myMembership === Membership.Join
                 ? _t("room|view_failed_enable_video_rooms")
                 : _t("room|join_failed_enable_video_rooms");
 
diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx
index e0baf41f19..4eb1af107d 100644
--- a/src/components/views/rooms/RoomTile.tsx
+++ b/src/components/views/rooms/RoomTile.tsx
@@ -124,7 +124,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
     private get showContextMenu(): boolean {
         return (
             this.props.tag !== DefaultTagID.Invite &&
-            this.props.room.getMyMembership() !== "knock" &&
+            this.props.room.getMyMembership() !== Membership.Knock &&
             !isKnockDenied(this.props.room) &&
             shouldShowComponent(UIComponent.RoomOptionsMenu)
         );
@@ -387,7 +387,7 @@ export class RoomTile extends React.PureComponent<ClassProps, State> {
             mx_RoomTile: true,
             mx_RoomTile_sticky:
                 SettingsStore.getValue("feature_ask_to_join") &&
-                (this.props.room.getMyMembership() === "knock" || isKnockDenied(this.props.room)),
+                (this.props.room.getMyMembership() === Membership.Knock || isKnockDenied(this.props.room)),
             mx_RoomTile_selected: this.state.selected,
             mx_RoomTile_hasMenuOpen: !!(this.state.generalMenuPosition || this.state.notificationsMenuPosition),
             mx_RoomTile_minimized: this.props.isMinimized,
diff --git a/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.tsx
index 8e990d7d33..7ec9a2a571 100644
--- a/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.tsx
+++ b/src/components/views/settings/tabs/room/GeneralRoomSettingsTab.tsx
@@ -73,7 +73,7 @@ export default class GeneralRoomSettingsTab extends React.Component<IProps, ISta
         ) : null;
 
         let leaveSection;
-        if (room.getMyMembership() === "join") {
+        if (room.getMyMembership() === Membership.Join) {
             leaveSection = (
                 <SettingsSubsection heading={_t("action|leave_room")}>
                     <AccessibleButton kind="danger" onClick={this.onLeaveClick}>
diff --git a/src/components/views/settings/tabs/room/PeopleRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/PeopleRoomSettingsTab.tsx
index 418addfaf9..3350e7c5ca 100644
--- a/src/components/views/settings/tabs/room/PeopleRoomSettingsTab.tsx
+++ b/src/components/views/settings/tabs/room/PeopleRoomSettingsTab.tsx
@@ -145,7 +145,7 @@ export const PeopleRoomSettingsTab: VFC<{ room: Room }> = ({ room }) => {
     const knockMembers = useTypedEventEmitterState(
         room,
         RoomStateEvent.Update,
-        useCallback(() => room.getMembersWithMembership("knock"), [room]),
+        useCallback(() => room.getMembersWithMembership(Membership.Knock), [room]),
     );
 
     return (
diff --git a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx
index 5f03e7f950..774813a776 100644
--- a/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx
+++ b/src/components/views/settings/tabs/room/RolesRoomSettingsTab.tsx
@@ -413,7 +413,7 @@ export default class RolesRoomSettingsTab extends React.Component<IProps> {
             }
         }
 
-        const banned = room.getMembersWithMembership("ban");
+        const banned = room.getMembersWithMembership(Membership.Ban);
         let bannedUsersSection: JSX.Element | undefined;
         if (banned?.length) {
             const canBanUsers = currentUserLevel >= banLevel;
diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx
index 6d32f683b7..4b1b01cd91 100644
--- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx
+++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx
@@ -121,12 +121,12 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
         MatrixClientPeg.safeGet().removeListener(RoomEvent.MyMembership, this.onMyMembership);
     }
 
-    private onMyMembership = (room: Room, membership: string): void => {
+    private onMyMembership = (room: Room, membership: Membership): void => {
         if (room.isSpaceRoom()) {
             return;
         }
 
-        if (membership === "invite") {
+        if (membership === Membership.Invite) {
             this.addInvitedRoom(room);
         } else if (this.state.invitedRoomIds.has(room.roomId)) {
             // The user isn't invited anymore
@@ -167,7 +167,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
         return MatrixClientPeg.safeGet()
             .getRooms()
             .filter((r) => {
-                return r.hasMembershipState(MatrixClientPeg.safeGet().getUserId()!, "invite");
+                return r.hasMembershipState(MatrixClientPeg.safeGet().getUserId()!, Membership.Invite);
             });
     };
 
diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx
index 315cba3c1c..4b53181d41 100644
--- a/src/components/views/spaces/SpaceTreeLevel.tsx
+++ b/src/components/views/spaces/SpaceTreeLevel.tsx
@@ -99,7 +99,7 @@ export const SpaceButton = <T extends keyof JSX.IntrinsicElements>({
     let notifBadge;
     if (spaceKey && notificationState) {
         let ariaLabel = _t("a11y_jump_first_unread_room");
-        if (space?.getMyMembership() === "invite") {
+        if (space?.getMyMembership() === Membership.Invite) {
             ariaLabel = _t("a11y|jump_first_invite");
         }
 
@@ -325,7 +325,7 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
             hasSubSpaces: this.state.childSpaces?.length,
         });
 
-        const isInvite = space.getMyMembership() === "invite";
+        const isInvite = space.getMyMembership() === Membership.Invite;
 
         const notificationState = isInvite
             ? StaticNotificationState.forSymbol("!", NotificationLevel.Highlight)
@@ -378,7 +378,9 @@ export class SpaceItem extends React.PureComponent<IItemProps, IItemState> {
                     isNarrow={isPanelCollapsed}
                     size={isNested ? "24px" : "32px"}
                     onKeyDown={this.onKeyDown}
-                    ContextMenuComponent={this.props.space.getMyMembership() === "join" ? SpaceContextMenu : undefined}
+                    ContextMenuComponent={
+                        this.props.space.getMyMembership() === Membership.Join ? SpaceContextMenu : undefined
+                    }
                 >
                     {toggleCollapseButton}
                 </SpaceButton>
diff --git a/src/hooks/useRoomMembers.ts b/src/hooks/useRoomMembers.ts
index c0436562df..97c9255463 100644
--- a/src/hooks/useRoomMembers.ts
+++ b/src/hooks/useRoomMembers.ts
@@ -81,8 +81,8 @@ export const useRoomMemberCount = (
 };
 
 // Hook to simplify watching the local user's membership in a room
-export const useMyRoomMembership = (room: Room): string => {
-    const [membership, setMembership] = useState<string>(room.getMyMembership());
+export const useMyRoomMembership = (room: Room): Membership => {
+    const [membership, setMembership] = useState<Membership>(room.getMyMembership());
     useTypedEventEmitter(room, RoomEvent.MyMembership, () => {
         setMembership(room.getMyMembership());
     });
diff --git a/src/hooks/useSpaceResults.ts b/src/hooks/useSpaceResults.ts
index f21e071610..bb209a4403 100644
--- a/src/hooks/useSpaceResults.ts
+++ b/src/hooks/useSpaceResults.ts
@@ -57,7 +57,7 @@ export const useSpaceResults = (space: Room | undefined, query: string): [Hierar
         return rooms?.filter((r) => {
             return (
                 r.room_type !== RoomType.Space &&
-                cli.getRoom(r.room_id)?.getMyMembership() !== "join" &&
+                cli.getRoom(r.room_id)?.getMyMembership() !== Membership.Join &&
                 (normalize(r.name || "").includes(normalizedQuery) || (r.canonical_alias || "").includes(lcQuery))
             );
         });
diff --git a/src/indexing/EventIndex.ts b/src/indexing/EventIndex.ts
index 69cb0e1218..ea739ffb8d 100644
--- a/src/indexing/EventIndex.ts
+++ b/src/indexing/EventIndex.ts
@@ -533,7 +533,7 @@ export default class EventIndex extends EventEmitter {
             const profiles: Record<string, IMatrixProfile> = {};
 
             stateEvents.forEach((ev) => {
-                if (ev.getContent().membership === "join") {
+                if (ev.getContent().membership === Membership.Join) {
                     profiles[ev.getSender()!] = {
                         displayname: ev.getContent().displayname,
                         avatar_url: ev.getContent().avatar_url,
@@ -754,7 +754,7 @@ export default class EventIndex extends EventEmitter {
             // This is sets the avatar URL.
             const memberEvent = eventMapper({
                 content: {
-                    membership: "join",
+                    membership: Membership.Join,
                     avatar_url: e.profile.avatar_url,
                     displayname: e.profile.displayname,
                 },
diff --git a/src/models/Call.ts b/src/models/Call.ts
index 369f712534..d9560aa04d 100644
--- a/src/models/Call.ts
+++ b/src/models/Call.ts
@@ -307,8 +307,8 @@ export abstract class Call extends TypedEventEmitter<CallEvent, CallEventHandler
         this.emit(CallEvent.Destroy);
     }
 
-    private onMyMembership = async (_room: Room, membership: string): Promise<void> => {
-        if (membership !== "join") this.setDisconnected();
+    private onMyMembership = async (_room: Room, membership: Membership): Promise<void> => {
+        if (membership !== Membership.Join) this.setDisconnected();
     };
 
     private onStopMessaging = (uid: string): void => {
@@ -386,7 +386,7 @@ export class JitsiCall extends Call {
                 devices = devices.filter((d) => d !== this.client.getDeviceId());
             }
             // Must have a connected device and still be joined to the room
-            if (devices.length > 0 && member?.membership === "join") {
+            if (devices.length > 0 && member?.membership === Membership.Join) {
                 participants.set(member, new Set(devices));
                 if (expiresAt < allExpireAt) allExpireAt = expiresAt;
             }
@@ -416,7 +416,7 @@ export class JitsiCall extends Call {
      *     returns null, the update is skipped.
      */
     private async updateDevices(fn: (devices: string[]) => string[] | null): Promise<void> {
-        if (this.room.getMyMembership() !== "join") return;
+        if (this.room.getMyMembership() !== Membership.Join) return;
 
         const event = this.room.currentState.getStateEvents(JitsiCall.MEMBER_EVENT_TYPE, this.client.getUserId()!);
         const content = event?.getContent<JitsiCallMemberContent>();
diff --git a/src/shouldHideEvent.ts b/src/shouldHideEvent.ts
index dcfc5d920c..54cd7040a7 100644
--- a/src/shouldHideEvent.ts
+++ b/src/shouldHideEvent.ts
@@ -39,10 +39,10 @@ function memberEventDiff(ev: MatrixEvent): IDiff {
     const prevContent = ev.getPrevContent();
 
     const isMembershipChanged = content.membership !== prevContent.membership;
-    diff.isJoin = isMembershipChanged && content.membership === "join";
-    diff.isPart = isMembershipChanged && content.membership === "leave" && ev.getStateKey() === ev.getSender();
+    diff.isJoin = isMembershipChanged && content.membership === Membership.Join;
+    diff.isPart = isMembershipChanged && content.membership === Membership.Leave && ev.getStateKey() === ev.getSender();
 
-    const isJoinToJoin = !isMembershipChanged && content.membership === "join";
+    const isJoinToJoin = !isMembershipChanged && content.membership === Membership.Join;
     diff.isDisplaynameChange = isJoinToJoin && content.displayname !== prevContent.displayname;
     diff.isAvatarChange = isJoinToJoin && content.avatar_url !== prevContent.avatar_url;
     return diff;
diff --git a/src/stores/BreadcrumbsStore.ts b/src/stores/BreadcrumbsStore.ts
index bacbfe97be..631b99a72d 100644
--- a/src/stores/BreadcrumbsStore.ts
+++ b/src/stores/BreadcrumbsStore.ts
@@ -91,7 +91,7 @@ export class BreadcrumbsStore extends AsyncStoreWithClient<IState> {
                 // The tests might not result in a valid room object.
                 const room = this.matrixClient.getRoom(payload.room_id);
                 const membership = room?.getMyMembership();
-                if (room && membership === "join") await this.appendRoom(room);
+                if (room && membership === Membership.Join) await this.appendRoom(room);
             }
         } else if (payload.action === Action.JoinRoom) {
             const room = this.matrixClient.getRoom(payload.roomId);
diff --git a/src/stores/MemberListStore.ts b/src/stores/MemberListStore.ts
index d7c50e0078..133bcbd5c6 100644
--- a/src/stores/MemberListStore.ts
+++ b/src/stores/MemberListStore.ts
@@ -90,7 +90,7 @@ export class MemberListStore {
             // pull straight from the server. Don't use a since token as we don't have earlier deltas
             // accumulated.
             room.currentState.markOutOfBandMembersStarted();
-            const response = await this.stores.client!.members(roomId, undefined, "leave");
+            const response = await this.stores.client!.members(roomId, undefined, Membership.Leave);
             const memberEvents = response.chunk.map(this.stores.client!.getEventMapper());
             room.currentState.setOutOfBandMembers(memberEvents);
         } else {
@@ -167,7 +167,7 @@ export class MemberListStore {
             invited: [],
         };
         members.forEach((m) => {
-            if (m.membership !== "join" && m.membership !== "invite") {
+            if (m.membership !== Membership.Join && m.membership !== Membership.Invite) {
                 return; // bail early for left/banned users
             }
             if (query) {
@@ -179,10 +179,10 @@ export class MemberListStore {
                 }
             }
             switch (m.membership) {
-                case "join":
+                case Membership.Join:
                     result.joined.push(m);
                     break;
-                case "invite":
+                case Membership.Invite:
                     result.invited.push(m);
                     break;
             }
diff --git a/src/stores/OwnBeaconStore.ts b/src/stores/OwnBeaconStore.ts
index e2b4a0c07b..9cb9efe4be 100644
--- a/src/stores/OwnBeaconStore.ts
+++ b/src/stores/OwnBeaconStore.ts
@@ -313,7 +313,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
         // in PSF-797
 
         // stop watching beacons in rooms where user is no longer a member
-        if (member.membership === "leave" || member.membership === "ban") {
+        if (member.membership === Membership.Leave || member.membership === Membership.Ban) {
             this.beaconsByRoomId.get(roomState.roomId)?.forEach(this.removeBeacon);
             this.beaconsByRoomId.delete(roomState.roomId);
         }
diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx
index 4b7b165b44..17c3035f3e 100644
--- a/src/stores/RoomViewStore.tsx
+++ b/src/stores/RoomViewStore.tsx
@@ -607,7 +607,7 @@ export class RoomViewStore extends EventEmitter {
     private getInvitingUserId(roomId: string): string | undefined {
         const cli = MatrixClientPeg.safeGet();
         const room = cli.getRoom(roomId);
-        if (room?.getMyMembership() === "invite") {
+        if (room?.getMyMembership() === Membership.Invite) {
             const myMember = room.getMember(cli.getSafeUserId());
             const inviteEvent = myMember ? myMember.events.member : null;
             return inviteEvent?.getSender();
diff --git a/src/stores/notifications/RoomNotificationState.ts b/src/stores/notifications/RoomNotificationState.ts
index 0503485584..2fd1c18208 100644
--- a/src/stores/notifications/RoomNotificationState.ts
+++ b/src/stores/notifications/RoomNotificationState.ts
@@ -96,7 +96,8 @@ export class RoomNotificationState extends NotificationState implements IDestroy
         const { level, symbol, count } = RoomNotifs.determineUnreadState(this.room, undefined, this.includeThreads);
         const muted =
             RoomNotifs.getRoomNotifsState(this.room.client, this.room.roomId) === RoomNotifs.RoomNotifState.Mute;
-        const knocked = SettingsStore.getValue("feature_ask_to_join") && this.room.getMyMembership() === "knock";
+        const knocked =
+            SettingsStore.getValue("feature_ask_to_join") && this.room.getMyMembership() === Membership.Knock;
         this._level = level;
         this._symbol = symbol;
         this._count = count;
diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts
index b6e65fada7..99b8ee2b28 100644
--- a/src/stores/room-list/RoomListStore.ts
+++ b/src/stores/room-list/RoomListStore.ts
@@ -350,7 +350,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> implements
     }
 
     private async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<any> {
-        if (cause === RoomUpdateCause.NewRoom && room.getMyMembership() === "invite") {
+        if (cause === RoomUpdateCause.NewRoom && room.getMyMembership() === Membership.Invite) {
             // Let the visibility provider know that there is a new invited room. It would be nice
             // if this could just be an event that things listen for but the point of this is that
             // we delay doing anything about this room until the VoipUserMapper had had a chance
diff --git a/src/stores/room-list/algorithms/Algorithm.ts b/src/stores/room-list/algorithms/Algorithm.ts
index 0645b7e49b..06e28b49d3 100644
--- a/src/stores/room-list/algorithms/Algorithm.ts
+++ b/src/stores/room-list/algorithms/Algorithm.ts
@@ -172,7 +172,7 @@ export class Algorithm extends EventEmitter {
     }
 
     private doUpdateStickyRoom(val: Room | null): void {
-        if (val?.isSpaceRoom() && val.getMyMembership() !== "invite") {
+        if (val?.isSpaceRoom() && val.getMyMembership() !== Membership.Invite) {
             // no-op sticky rooms for spaces - they're effectively virtual rooms
             val = null;
         }
diff --git a/src/stores/spaces/SpaceStore.ts b/src/stores/spaces/SpaceStore.ts
index 0075e99231..c9aa729129 100644
--- a/src/stores/spaces/SpaceStore.ts
+++ b/src/stores/spaces/SpaceStore.ts
@@ -255,8 +255,8 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
             // else view space home or home depending on what is being clicked on
             if (
                 roomId &&
-                cliSpace?.getMyMembership() !== "invite" &&
-                this.matrixClient.getRoom(roomId)?.getMyMembership() === "join" &&
+                cliSpace?.getMyMembership() !== Membership.Invite &&
+                this.matrixClient.getRoom(roomId)?.getMyMembership() === Membership.Join &&
                 this.isRoomInSpace(space, roomId)
             ) {
                 defaultDispatcher.dispatch<ViewRoomPayload>({
@@ -325,7 +325,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
                 .filter((roomInfo) => {
                     return (
                         roomInfo.room_type !== RoomType.Space &&
-                        this.matrixClient?.getRoom(roomInfo.room_id)?.getMyMembership() !== "join"
+                        this.matrixClient.getRoom(roomInfo.room_id)?.getMyMembership() !== Membership.Join
                     );
                 })
                 .map((roomInfo) => ({
@@ -368,7 +368,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
                     return history[history.length - 1];
                 })
                 .filter((room) => {
-                    return room?.getMyMembership() === "join" || room?.getMyMembership() === "invite";
+                    return room?.getMyMembership() === Membership.Join || room?.getMyMembership() === Membership.Invite;
                 }) || []
         );
     }
@@ -379,7 +379,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
 
     public getChildSpaces(spaceId: string): Room[] {
         // don't show invited subspaces as they surface at the top level for better visibility
-        return this.getChildren(spaceId).filter((r) => r.isSpaceRoom() && r.getMyMembership() === "join");
+        return this.getChildren(spaceId).filter((r) => r.isSpaceRoom() && r.getMyMembership() === Membership.Join);
     }
 
     public getParents(roomId: string, canonicalOnly = false): Room[] {
@@ -593,7 +593,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
     private rebuildParentMap = (): void => {
         if (!this.matrixClient) return;
         const joinedSpaces = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor).filter((r) => {
-            return r.isSpaceRoom() && r.getMyMembership() === "join";
+            return r.isSpaceRoom() && r.getMyMembership() === Membership.Join;
         });
 
         this.parentMap = new EnhancedMap<string, Set<string>>();
@@ -717,12 +717,12 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
         return (
             !this.parentMap.get(room.roomId)?.size || // put all orphaned rooms in the Home Space
             !!DMRoomMap.shared().getUserIdForRoomId(room.roomId) || // put all DMs in the Home Space
-            room.getMyMembership() === "invite"
+            room.getMyMembership() === Membership.Invite
         ); // put all invites in the Home Space
     };
 
-    private static isInSpace(member?: RoomMember | null): boolean {
-        return member?.membership === "join" || member?.membership === "invite";
+    private static isInSpace(member: RoomMember): boolean {
+        return member.membership === Membership.Join || member.membership === Membership.Invite;
     }
 
     // Method for resolving the impact of a single user's membership change in the given Space and its hierarchy
@@ -766,7 +766,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
 
         const hiddenChildren = new EnhancedMap<string, Set<string>>();
         visibleRooms.forEach((room) => {
-            if (!["join", "invite"].includes(room.getMyMembership())) return;
+            if (![Membership.Join, Membership.Invite].includes(room.getMyMembership())) return;
             this.getParents(room.roomId).forEach((parent) => {
                 hiddenChildren.getOrCreate(parent.roomId, new Set()).add(room.roomId);
             });
@@ -796,7 +796,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
                     space
                         ?.getMembers()
                         .filter((m) => {
-                            return m.membership === "join" || m.membership === "invite";
+                            return m.membership === Membership.Join || m.membership === Membership.Invite;
                         })
                         .map((m) => m.userId),
                 );
@@ -924,7 +924,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
         if (!room.isSpaceRoom()) {
             this.onRoomsUpdate();
 
-            if (membership === "join") {
+            if (membership === Membership.Join) {
                 // the user just joined a room, remove it from the suggested list if it was there
                 const numSuggestedRooms = this._suggestedRooms.length;
                 this._suggestedRooms = this._suggestedRooms.filter((r) => r.room_id !== room.roomId);
@@ -935,7 +935,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
                 }
 
                 // if the room currently being viewed was just joined then switch to its related space
-                if (newMembership === "join" && room.roomId === SdkContextClass.instance.roomViewStore.getRoomId()) {
+                if (
+                    newMembership === Membership.Join &&
+                    room.roomId === SdkContextClass.instance.roomViewStore.getRoomId()
+                ) {
                     this.switchSpaceIfNeeded(room.roomId);
                 }
             }
@@ -943,13 +946,13 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
         }
 
         // Space
-        if (membership === "invite") {
+        if (membership === Membership.Invite) {
             const len = this._invitedSpaces.size;
             this._invitedSpaces.add(room);
             if (len !== this._invitedSpaces.size) {
                 this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces);
             }
-        } else if (oldMembership === "invite" && membership !== "join") {
+        } else if (oldMembership === Membership.Invite && membership !== Membership.Join) {
             if (this._invitedSpaces.delete(room)) {
                 this.emit(UPDATE_INVITED_SPACES, this.invitedSpaces);
             }
@@ -962,10 +965,10 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
             this.emit(room.roomId);
         }
 
-        if (membership === "join" && room.roomId === SdkContextClass.instance.roomViewStore.getRoomId()) {
+        if (membership === Membership.Join && room.roomId === SdkContextClass.instance.roomViewStore.getRoomId()) {
             // if the user was looking at the space and then joined: select that space
             this.setActiveSpace(room.roomId, false);
-        } else if (membership === "leave" && room.roomId === this.activeSpace) {
+        } else if (membership === Membership.Leave && room.roomId === this.activeSpace) {
             // user's active space has gone away, go back to home
             this.goToFirstSpace(true);
         }
@@ -1000,7 +1003,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient<IState> {
 
                 if (
                     room.roomId === this.activeSpace && // current space
-                    target?.getMyMembership() !== "join" && // target not joined
+                    target?.getMyMembership() !== Membership.Join && // target not joined
                     ev.getPrevContent().suggested !== ev.getContent().suggested // suggested flag changed
                 ) {
                     this.loadSuggestedRooms(room);
diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts
index 64e9a980d1..8473244cf6 100644
--- a/src/stores/widgets/StopGapWidget.ts
+++ b/src/stores/widgets/StopGapWidget.ts
@@ -550,7 +550,7 @@ export class StopGapWidget extends EventEmitter {
         const evId = ev.getId();
         if (evRoomId && evId) {
             const room = this.client.getRoom(evRoomId);
-            if (room && room.getMyMembership() === "join" && !isRelationToUnknown) {
+            if (room && room.getMyMembership() === Membership.Join && !isRelationToUnknown) {
                 this.readUpToMap[evRoomId] = evId;
             }
         }
diff --git a/src/utils/DMRoomMap.ts b/src/utils/DMRoomMap.ts
index eafbe07d5d..94ce251279 100644
--- a/src/utils/DMRoomMap.ts
+++ b/src/utils/DMRoomMap.ts
@@ -172,7 +172,7 @@ export default class DMRoomMap {
 
         const joinedRooms = commonRooms
             .map((r) => this.matrixClient.getRoom(r))
-            .filter((r) => r && r.getMyMembership() === "join");
+            .filter((r) => r && r.getMyMembership() === Membership.Join);
 
         return joinedRooms[0];
     }
diff --git a/src/utils/MultiInviter.ts b/src/utils/MultiInviter.ts
index de8ea1d7bc..cf25786874 100644
--- a/src/utils/MultiInviter.ts
+++ b/src/utils/MultiInviter.ts
@@ -159,17 +159,17 @@ export default class MultiInviter {
             if (!room) throw new Error("Room not found");
 
             const member = room.getMember(addr);
-            if (member?.membership === "join") {
+            if (member?.membership === Membership.Join) {
                 throw new MatrixError({
                     errcode: USER_ALREADY_JOINED,
                     error: "Member already joined",
                 });
-            } else if (member?.membership === "invite") {
+            } else if (member?.membership === Membership.Invite) {
                 throw new MatrixError({
                     errcode: USER_ALREADY_INVITED,
                     error: "Member already invited",
                 });
-            } else if (member?.membership === "ban") {
+            } else if (member?.membership === Membership.Ban) {
                 let proceed = false;
                 // Check if we can unban the invitee.
                 // See https://spec.matrix.org/v1.7/rooms/v10/#authorization-rules, particularly 4.5.3 and 4.5.4.
@@ -177,8 +177,8 @@ export default class MultiInviter {
                 if (
                     !!ourMember &&
                     member.powerLevel < ourMember.powerLevel &&
-                    room.currentState.hasSufficientPowerLevelFor("ban", ourMember.powerLevel) &&
-                    room.currentState.hasSufficientPowerLevelFor("kick", ourMember.powerLevel)
+                    room.currentState.hasSufficientPowerLevelFor(Membership.Ban, ourMember.powerLevel) &&
+                    room.currentState.hasSufficientPowerLevelFor(Membership.Kick, ourMember.powerLevel)
                 ) {
                     const { finished } = Modal.createDialog(ConfirmUserActionDialog, {
                         member,
diff --git a/src/utils/RoomUpgrade.ts b/src/utils/RoomUpgrade.ts
index 88fc904541..6f6d6e88c0 100644
--- a/src/utils/RoomUpgrade.ts
+++ b/src/utils/RoomUpgrade.ts
@@ -67,7 +67,10 @@ export async function upgradeRoom(
 
     let toInvite: string[] = [];
     if (inviteUsers) {
-        toInvite = [...room.getMembersWithMembership("join"), ...room.getMembersWithMembership("invite")]
+        toInvite = [
+            ...room.getMembersWithMembership(Membership.Join),
+            ...room.getMembersWithMembership(Membership.Invite),
+        ]
             .map((m) => m.userId)
             .filter((m) => m !== cli.getUserId());
     }
diff --git a/src/utils/SortMembers.ts b/src/utils/SortMembers.ts
index 534fe6a82e..adc99defaa 100644
--- a/src/utils/SortMembers.ts
+++ b/src/utils/SortMembers.ts
@@ -52,7 +52,7 @@ function joinedRooms(cli: MatrixClient): Room[] {
     return (
         cli
             .getRooms()
-            .filter((r) => r.getMyMembership() === "join")
+            .filter((r) => r.getMyMembership() === Membership.Join)
             // Skip low priority rooms and DMs
             .filter((r) => !DMRoomMap.shared().getUserIdForRoomId(r.roomId))
             .filter((r) => !Object.keys(r.tags).includes("m.lowpriority"))
diff --git a/src/utils/WidgetUtils.ts b/src/utils/WidgetUtils.ts
index 11918d1458..e6c6d777e9 100644
--- a/src/utils/WidgetUtils.ts
+++ b/src/utils/WidgetUtils.ts
@@ -83,7 +83,7 @@ export default class WidgetUtils {
             return false;
         }
 
-        if (room.getMyMembership() !== "join") {
+        if (room.getMyMembership() !== Membership.Join) {
             logger.warn(`User ${me} is not in room ${roomId}`);
             return false;
         }
diff --git a/src/utils/dm/createDmLocalRoom.ts b/src/utils/dm/createDmLocalRoom.ts
index ac14834303..108ba0492f 100644
--- a/src/utils/dm/createDmLocalRoom.ts
+++ b/src/utils/dm/createDmLocalRoom.ts
@@ -76,7 +76,7 @@ export async function createDmLocalRoom(client: MatrixClient, targets: Member[])
             type: EventType.RoomMember,
             content: {
                 displayname: userId,
-                membership: "join",
+                membership: MembershipJoin,
             },
             state_key: userId,
             user_id: userId,
@@ -93,7 +93,7 @@ export async function createDmLocalRoom(client: MatrixClient, targets: Member[])
                 content: {
                     displayname: target.name,
                     avatar_url: target.getMxcAvatarUrl() ?? undefined,
-                    membership: "invite",
+                    membership: Membership.Invite,
                     isDirect: true,
                 },
                 state_key: target.userId,
@@ -108,7 +108,7 @@ export async function createDmLocalRoom(client: MatrixClient, targets: Member[])
                 content: {
                     displayname: target.name,
                     avatar_url: target.getMxcAvatarUrl() ?? undefined,
-                    membership: "join",
+                    membership: Membership.Join,
                 },
                 state_key: target.userId,
                 sender: target.userId,
@@ -118,7 +118,7 @@ export async function createDmLocalRoom(client: MatrixClient, targets: Member[])
     });
 
     localRoom.targets = targets;
-    localRoom.updateMyMembership("join");
+    localRoom.updateMyMembership(Membership.Join);
     localRoom.addLiveEvents(events);
     localRoom.currentState.setStateEvents(events);
     localRoom.name = localRoom.getDefaultRoomName(client.getUserId()!);
diff --git a/src/utils/dm/findDMForUser.ts b/src/utils/dm/findDMForUser.ts
index 92575d41be..34bfe8caa8 100644
--- a/src/utils/dm/findDMForUser.ts
+++ b/src/utils/dm/findDMForUser.ts
@@ -42,7 +42,7 @@ function extractSuitableRoom(rooms: Room[], userId: string, findRoomWithThirdpar
             // a DM is a room of two people that contains those two people exactly. This does mean
             // that bots, assistants, etc will ruin a room's DM-ness, though this is a problem for
             // canonical DMs to solve.
-            if (r && r.getMyMembership() === "join") {
+            if (r && r.getMyMembership() === Membership.Join) {
                 if (isLocalRoom(r)) return false;
 
                 const functionalUsers = getFunctionalMembers(r);
diff --git a/src/utils/membership.ts b/src/utils/membership.ts
index df012e442b..42d8796048 100644
--- a/src/utils/membership.ts
+++ b/src/utils/membership.ts
@@ -65,10 +65,13 @@ export function splitRoomsByMembership(rooms: Room[]): MembershipSplit {
     return split;
 }
 
-export function getEffectiveMembership(membership: string): EffectiveMembership {
-    if (membership === "invite") {
+export function getEffectiveMembership(membership: Membership): EffectiveMembership {
+    if (membership === Membership.Invite) {
         return EffectiveMembership.Invite;
-    } else if (membership === "join" || (SettingsStore.getValue("feature_ask_to_join") && membership === "knock")) {
+    } else if (
+        membership === Membership.Join ||
+        (SettingsStore.getValue("feature_ask_to_join") && membership === Membership.Knock)
+    ) {
         return EffectiveMembership.Join;
     } else {
         // Probably a leave, kick, or ban
@@ -81,7 +84,7 @@ export function isKnockDenied(room: Room): boolean | undefined {
     const member = memberId ? room.getMember(memberId) : null;
     const previousMembership = member?.events.member?.getPrevContent().membership;
 
-    return member?.isKicked() && previousMembership === "knock";
+    return member?.isKicked() && previousMembership === Membership.Knock;
 }
 
 export function getEffectiveMembershipTag(room: Room, membership?: string): EffectiveMembership {
@@ -90,7 +93,7 @@ export function getEffectiveMembershipTag(room: Room, membership?: string): Effe
         : getEffectiveMembership(membership ?? room.getMyMembership());
 }
 
-export function isJoinedOrNearlyJoined(membership: string): boolean {
+export function isJoinedOrNearlyJoined(membership: Membership): boolean {
     const effective = getEffectiveMembership(membership);
     return effective === EffectiveMembership.Join || effective === EffectiveMembership.Invite;
 }
diff --git a/src/utils/permalinks/Permalinks.ts b/src/utils/permalinks/Permalinks.ts
index 9af7476a39..e30230dd7a 100644
--- a/src/utils/permalinks/Permalinks.ts
+++ b/src/utils/permalinks/Permalinks.ts
@@ -178,7 +178,7 @@ export class RoomPermalinkCreator {
                     const entries = Object.entries(users);
                     const allowedEntries = entries.filter(([userId]) => {
                         const member = this.room?.getMember(userId);
-                        if (!member || member.membership !== "join") {
+                        if (!member || member.membership !== Membership.Join) {
                             return false;
                         }
                         const serverName = getServerName(userId);
diff --git a/src/utils/room/canInviteTo.ts b/src/utils/room/canInviteTo.ts
index 55265e6cc8..9bda1c45b2 100644
--- a/src/utils/room/canInviteTo.ts
+++ b/src/utils/room/canInviteTo.ts
@@ -29,5 +29,5 @@ export function canInviteTo(room: Room): boolean {
     const canInvite =
         !!room.canInvite(client.getSafeUserId()) || !!(room.isSpaceRoom() && room.getJoinRule() === JoinRule.Public);
 
-    return canInvite && room.getMyMembership() === "join" && shouldShowComponent(UIComponent.InviteUsers);
+    return canInvite && room.getMyMembership() === Membership.Join && shouldShowComponent(UIComponent.InviteUsers);
 }
diff --git a/src/utils/space.tsx b/src/utils/space.tsx
index d6cfc5a4ff..218c43bfc1 100644
--- a/src/utils/space.tsx
+++ b/src/utils/space.tsx
@@ -40,7 +40,7 @@ import { SdkContextClass } from "../contexts/SDKContext";
 export const shouldShowSpaceSettings = (space: Room): boolean => {
     const userId = space.client.getUserId()!;
     return (
-        space.getMyMembership() === "join" &&
+        space.getMyMembership() === Membership.Join &&
         (space.currentState.maySendStateEvent(EventType.RoomAvatar, userId) ||
             space.currentState.maySendStateEvent(EventType.RoomName, userId) ||
             space.currentState.maySendStateEvent(EventType.RoomTopic, userId) ||
@@ -85,7 +85,7 @@ export const showCreateNewRoom = async (space: Room, type?: RoomType): Promise<b
 };
 
 export const shouldShowSpaceInvite = (space: Room): boolean =>
-    ((space?.getMyMembership() === "join" && space.canInvite(space.client.getUserId()!)) ||
+    ((space?.getMyMembership() === Membership.Join && space.canInvite(space.client.getUserId()!)) ||
         space.getJoinRule() === JoinRule.Public) &&
     shouldShowComponent(UIComponent.InviteUsers);
 
diff --git a/test/LegacyCallHandler-test.ts b/test/LegacyCallHandler-test.ts
index 9e31baad2a..489a00bd03 100644
--- a/test/LegacyCallHandler-test.ts
+++ b/test/LegacyCallHandler-test.ts
@@ -102,7 +102,7 @@ function mkStubDM(roomId: string, userId: string) {
             name: "Member",
             rawDisplayName: "Member",
             roomId: roomId,
-            membership: "join",
+            membership: Membership.Join,
             getAvatarUrl: () => "mxc://avatar.url/image.png",
             getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
         },
@@ -111,7 +111,7 @@ function mkStubDM(roomId: string, userId: string) {
             name: "Member",
             rawDisplayName: "Member",
             roomId: roomId,
-            membership: "join",
+            membership: Membership.Join,
             getAvatarUrl: () => "mxc://avatar.url/image.png",
             getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
         },
@@ -120,7 +120,7 @@ function mkStubDM(roomId: string, userId: string) {
             name: "Bot user",
             rawDisplayName: "Bot user",
             roomId: roomId,
-            membership: "join",
+            membership: Membership.Join,
             getAvatarUrl: () => "mxc://avatar.url/image.png",
             getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
         },
diff --git a/test/RoomNotifs-test.ts b/test/RoomNotifs-test.ts
index 371ffea910..3240a83ea3 100644
--- a/test/RoomNotifs-test.ts
+++ b/test/RoomNotifs-test.ts
@@ -277,7 +277,7 @@ describe("RoomNotifs test", () => {
         });
 
         it("indicates the user has been invited to a channel", async () => {
-            room.updateMyMembership("invite");
+            room.updateMyMembership(Membership.Invite);
 
             const { level, symbol, count } = determineUnreadState(room);
 
@@ -290,9 +290,15 @@ describe("RoomNotifs test", () => {
             jest.spyOn(SettingsStore, "getValue").mockImplementation((name) => {
                 return name === "feature_ask_to_join";
             });
-            const roomMember = mkRoomMember(room.roomId, MatrixClientPeg.get()!.getSafeUserId(), "leave", true, {
-                membership: "knock",
-            });
+            const roomMember = mkRoomMember(
+                room.roomId,
+                MatrixClientPeg.get()!.getSafeUserId(),
+                Membership.Leave,
+                true,
+                {
+                    membership: Membership.Knock,
+                },
+            );
             jest.spyOn(room, "getMember").mockReturnValue(roomMember);
             const { level, symbol, count } = determineUnreadState(room);
 
diff --git a/test/SlashCommands-test.tsx b/test/SlashCommands-test.tsx
index ae90d2bbc4..7da019af4c 100644
--- a/test/SlashCommands-test.tsx
+++ b/test/SlashCommands-test.tsx
@@ -162,7 +162,7 @@ describe("SlashCommands", () => {
         it("should warn about self demotion", async () => {
             setCurrentRoom();
             const member = new RoomMember(roomId, client.getSafeUserId());
-            member.membership = "join";
+            member.membership = Membership.Join;
             member.powerLevel = 100;
             room.getMember = () => member;
             command.run(client, roomId, null, `${client.getUserId()} 0`);
@@ -172,7 +172,7 @@ describe("SlashCommands", () => {
         it("should default to 50 if no powerlevel specified", async () => {
             setCurrentRoom();
             const member = new RoomMember(roomId, "@user:server");
-            member.membership = "join";
+            member.membership = Membership.Join;
             room.getMember = () => member;
             command.run(client, roomId, null, member.userId);
             expect(client.setPowerLevel).toHaveBeenCalledWith(roomId, member.userId, 50);
@@ -191,7 +191,7 @@ describe("SlashCommands", () => {
         it("should warn about self demotion", async () => {
             setCurrentRoom();
             const member = new RoomMember(roomId, client.getSafeUserId());
-            member.membership = "join";
+            member.membership = Membership.Join;
             member.powerLevel = 100;
             room.getMember = () => member;
             command.run(client, roomId, null, client.getSafeUserId());
@@ -366,7 +366,7 @@ describe("SlashCommands", () => {
     describe("/join", () => {
         beforeEach(() => {
             jest.spyOn(dispatcher, "dispatch");
-            command = findCommand("join")!;
+            command = findCommand(Membership.Join)!;
         });
 
         it("should return usage if no args", () => {
diff --git a/test/TextForEvent-test.ts b/test/TextForEvent-test.ts
index 4a8258879c..9e70b2ff51 100644
--- a/test/TextForEvent-test.ts
+++ b/test/TextForEvent-test.ts
@@ -504,12 +504,12 @@ describe("TextForEvent", () => {
                         type: "m.room.member",
                         sender: "@a:foo",
                         content: {
-                            membership: "join",
+                            membership: Membership.Join,
                             avatar_url: "b",
                             displayname: "Bob",
                         },
                         prev_content: {
-                            membership: "join",
+                            membership: Membership.Join,
                             avatar_url: "a",
                             displayname: "Andy",
                         },
diff --git a/test/components/structures/MessagePanel-test.tsx b/test/components/structures/MessagePanel-test.tsx
index 45fe3b4abe..00d55c371d 100644
--- a/test/components/structures/MessagePanel-test.tsx
+++ b/test/components/structures/MessagePanel-test.tsx
@@ -171,8 +171,8 @@ describe("MessagePanel", function () {
                     user: "@user:id",
                     target: bobMember,
                     ts: ts0 + i * 1000,
-                    mship: "join",
-                    prevMship: "join",
+                    mship: Membership.Join,
+                    prevMship: Membership.Join,
                     name: "A user",
                 }),
             );
@@ -205,8 +205,8 @@ describe("MessagePanel", function () {
                     user: "@user:id",
                     target: bobMember,
                     ts: ts0 + i * 1000,
-                    mship: "join",
-                    prevMship: "join",
+                    mship: Membership.Join,
+                    prevMship: Membership.Join,
                     name: "A user",
                 }),
             );
@@ -245,7 +245,7 @@ describe("MessagePanel", function () {
                 user: alice,
                 target: aliceMember,
                 ts: ts0 + 1,
-                mship: "join",
+                mship: Membership.Join,
                 name: "Alice",
             }),
             mkEvent({
@@ -285,7 +285,7 @@ describe("MessagePanel", function () {
                 skey: "@bob:example.org",
                 target: bobMember,
                 ts: ts0 + 5,
-                mship: "invite",
+                mship: Membership.Invite,
                 name: "Bob",
             }),
         ];
@@ -542,8 +542,8 @@ describe("MessagePanel", function () {
                 user: "@user:id",
                 target: bobMember,
                 ts: Date.now(),
-                mship: "join",
-                prevMship: "join",
+                mship: Membership.Join,
+                prevMship: Membership.Join,
                 name: "A user",
             }),
         ];
@@ -571,8 +571,8 @@ describe("MessagePanel", function () {
                 user: "@user:id",
                 target: bobMember,
                 ts: Date.now(),
-                mship: "join",
-                prevMship: "join",
+                mship: Membership.Join,
+                prevMship: Membership.Join,
                 name: "A user",
             }),
             ...events,
@@ -695,8 +695,8 @@ describe("MessagePanel", function () {
         for (let i = 0; i < 100; i++) {
             events.push(
                 TestUtilsMatrix.mkMembership({
-                    mship: "join",
-                    prevMship: "join",
+                    mship: Membership.Join,
+                    prevMship: Membership.Join,
                     room: "!room:id",
                     user: "@user:id",
                     event: true,
@@ -716,8 +716,8 @@ describe("MessagePanel", function () {
         for (let i = 0; i < 100; i++) {
             events.push(
                 TestUtilsMatrix.mkMembership({
-                    mship: "join",
-                    prevMship: "join",
+                    mship: Membership.Join,
+                    prevMship: Membership.Join,
                     room: "!room:id",
                     user: "@user:id",
                     event: true,
diff --git a/test/components/structures/RoomView-test.tsx b/test/components/structures/RoomView-test.tsx
index 916e8a8225..13856c389a 100644
--- a/test/components/structures/RoomView-test.tsx
+++ b/test/components/structures/RoomView-test.tsx
@@ -238,7 +238,7 @@ describe("RoomView", () => {
     });
 
     it("updates url preview visibility on encryption state change", async () => {
-        room.getMyMembership = jest.fn().mockReturnValue("join");
+        room.getMyMembership = jest.fn().mockReturnValue(Membership.Join);
         // we should be starting unencrypted
         expect(cli.isCryptoEnabled()).toEqual(false);
         expect(cli.isRoomEncrypted(room.roomId)).toEqual(false);
@@ -583,7 +583,7 @@ describe("RoomView", () => {
         it("allows to cancel a join request", async () => {
             jest.spyOn(MatrixClientPeg, "safeGet").mockReturnValue(client);
             jest.spyOn(client, "leave").mockResolvedValue({});
-            jest.spyOn(room, "getMyMembership").mockReturnValue("knock");
+            jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Knock);
 
             await mountRoomView();
             fireEvent.click(screen.getByRole("button", { name: "Cancel request" }));
@@ -594,7 +594,7 @@ describe("RoomView", () => {
     });
 
     it("should close search results when edit is clicked", async () => {
-        room.getMyMembership = jest.fn().mockReturnValue("join");
+        room.getMyMembership = jest.fn().mockReturnValue(Membership.Join);
 
         const eventMapper = (obj: Partial<IEvent>) => new MatrixEvent(obj);
 
@@ -655,7 +655,7 @@ describe("RoomView", () => {
         const room2 = new Room(`!${roomCount++}:example.org`, cli, "@alice:example.org");
         rooms.set(room2.roomId, room2);
 
-        room.getMyMembership = jest.fn().mockReturnValue("join");
+        room.getMyMembership = jest.fn().mockReturnValue(Membership.Join);
 
         const eventMapper = (obj: Partial<IEvent>) => new MatrixEvent(obj);
 
diff --git a/test/components/structures/SpaceHierarchy-test.tsx b/test/components/structures/SpaceHierarchy-test.tsx
index 3b851c5a61..5e85ae2533 100644
--- a/test/components/structures/SpaceHierarchy-test.tsx
+++ b/test/components/structures/SpaceHierarchy-test.tsx
@@ -181,7 +181,7 @@ describe("SpaceHierarchy", () => {
         mocked(client.getRoom).mockImplementation(
             (roomId) => client.getRooms().find((room) => room.roomId === roomId) ?? null,
         );
-        [room1, room2, space1, room3].forEach((r) => mocked(r.getMyMembership).mockReturnValue("leave"));
+        [room1, room2, space1, room3].forEach((r) => mocked(r.getMyMembership).mockReturnValue(Membership.Leave));
 
         const hierarchyRoot: HierarchyRoom = {
             room_id: root.roomId,
diff --git a/test/components/structures/TimelinePanel-test.tsx b/test/components/structures/TimelinePanel-test.tsx
index a5312e43c5..be64b9baeb 100644
--- a/test/components/structures/TimelinePanel-test.tsx
+++ b/test/components/structures/TimelinePanel-test.tsx
@@ -988,8 +988,8 @@ describe("TimelinePanel", () => {
         events.forEach((event) => timelineSet.getLiveTimeline().addEvent(event, { toStartOfTimeline: true }));
 
         const roomMembership = mkMembership({
-            mship: "join",
-            prevMship: "join",
+            mship: Membership.Join,
+            prevMship: Membership.Join,
             user: authorId,
             room: room.roomId,
             event: true,
@@ -999,7 +999,7 @@ describe("TimelinePanel", () => {
         events.push(roomMembership);
 
         const member = new RoomMember(room.roomId, authorId);
-        member.membership = "join";
+        member.membership = Membership.Join;
 
         const roomState = new RoomState(room.roomId);
         jest.spyOn(roomState, "getMember").mockReturnValue(member);
diff --git a/test/components/views/context_menus/RoomContextMenu-test.tsx b/test/components/views/context_menus/RoomContextMenu-test.tsx
index 3fdd91874c..ca7236a2d5 100644
--- a/test/components/views/context_menus/RoomContextMenu-test.tsx
+++ b/test/components/views/context_menus/RoomContextMenu-test.tsx
@@ -106,7 +106,7 @@ describe("RoomContextMenu", () => {
     it("should render notification option for joined rooms", () => {
         const chamber = EchoChamber.forRoom(room);
         chamber.notificationVolume = RoomNotifState.Mute;
-        jest.spyOn(room, "getMyMembership").mockReturnValue("join");
+        jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Join);
         renderComponent();
 
         expect(
diff --git a/test/components/views/context_menus/RoomGeneralContextMenu-test.tsx b/test/components/views/context_menus/RoomGeneralContextMenu-test.tsx
index bb832612bf..f96425e2d9 100644
--- a/test/components/views/context_menus/RoomGeneralContextMenu-test.tsx
+++ b/test/components/views/context_menus/RoomGeneralContextMenu-test.tsx
@@ -105,7 +105,7 @@ describe("RoomGeneralContextMenu", () => {
     });
 
     it("does not render invite menu item when UIComponent customisations disable room invite", () => {
-        room.updateMyMembership("join");
+        room.updateMyMembership(Membership.Join);
         jest.spyOn(room, "canInvite").mockReturnValue(true);
         mocked(shouldShowComponent).mockReturnValue(false);
 
@@ -116,7 +116,7 @@ describe("RoomGeneralContextMenu", () => {
     });
 
     it("renders invite menu item when UIComponent customisations enables room invite", () => {
-        room.updateMyMembership("join");
+        room.updateMyMembership(Membership.Join);
         jest.spyOn(room, "canInvite").mockReturnValue(true);
         mocked(shouldShowComponent).mockReturnValue(true);
 
diff --git a/test/components/views/dialogs/ConfirmUserActionDialog-test.tsx b/test/components/views/dialogs/ConfirmUserActionDialog-test.tsx
index 1629a7a225..507157c090 100644
--- a/test/components/views/dialogs/ConfirmUserActionDialog-test.tsx
+++ b/test/components/views/dialogs/ConfirmUserActionDialog-test.tsx
@@ -25,7 +25,7 @@ describe("ConfirmUserActionDialog", () => {
         const { asFragment } = render(
             <ConfirmUserActionDialog
                 onFinished={jest.fn()}
-                member={mkRoomMember("123", "@user:test.com", "join")}
+                member={mkRoomMember("123", "@user:test.com", Membership.Join)}
                 action="Ban"
                 title="Ban this " // eg. 'Ban this user?'
             />,
diff --git a/test/components/views/dialogs/InviteDialog-test.tsx b/test/components/views/dialogs/InviteDialog-test.tsx
index 95e3131bf0..647e769f26 100644
--- a/test/components/views/dialogs/InviteDialog-test.tsx
+++ b/test/components/views/dialogs/InviteDialog-test.tsx
@@ -163,7 +163,7 @@ describe("InviteDialog", () => {
             mkMembership({
                 event: true,
                 room: roomId,
-                mship: "join",
+                mship: Membership.Join,
                 user: aliceId,
                 skey: aliceId,
             }),
diff --git a/test/components/views/dialogs/SpotlightDialog-test.tsx b/test/components/views/dialogs/SpotlightDialog-test.tsx
index 5ec1108bc4..a2ed2f2295 100644
--- a/test/components/views/dialogs/SpotlightDialog-test.tsx
+++ b/test/components/views/dialogs/SpotlightDialog-test.tsx
@@ -158,9 +158,9 @@ describe("Spotlight Dialog", () => {
     beforeEach(() => {
         mockedClient = mockClient({ rooms: [testPublicRoom], users: [testPerson] });
         testRoom = mkRoom(mockedClient, "!test23:example.com");
-        mocked(testRoom.getMyMembership).mockReturnValue("join");
+        mocked(testRoom.getMyMembership).mockReturnValue(Membership.Join);
         testLocalRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + "test23", mockedClient, mockedClient.getUserId()!);
-        testLocalRoom.updateMyMembership("join");
+        testLocalRoom.updateMyMembership(Membership.Join);
         mocked(mockedClient.getVisibleRooms).mockReturnValue([testRoom, testLocalRoom]);
 
         jest.spyOn(DMRoomMap, "shared").mockReturnValue({
@@ -169,7 +169,7 @@ describe("Spotlight Dialog", () => {
 
         testDM = mkRoom(mockedClient, testDMRoomId);
         testDM.name = "Chat with Alice";
-        mocked(testDM.getMyMembership).mockReturnValue("join");
+        mocked(testDM.getMyMembership).mockReturnValue(Membership.Join);
 
         mocked(DMRoomMap.shared().getUserIdForRoomId).mockImplementation((roomId: string) => {
             if (roomId === testDMRoomId) {
diff --git a/test/components/views/elements/EventListSummary-test.tsx b/test/components/views/elements/EventListSummary-test.tsx
index c57303dff2..19b1a55ec0 100644
--- a/test/components/views/elements/EventListSummary-test.tsx
+++ b/test/components/views/elements/EventListSummary-test.tsx
@@ -61,8 +61,8 @@ describe("EventListSummary", function () {
     interface MembershipEventParams {
         senderId?: string;
         userId?: string;
-        membership: string;
-        prevMembership?: string;
+        membership: Membership;
+        prevMembership?: Membership;
     }
     const generateMembershipEvent = (
         eventId: string,
@@ -145,7 +145,9 @@ describe("EventListSummary", function () {
     });
 
     it("renders expanded events if there are less than props.threshold", function () {
-        const events = generateEvents([{ userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" }]);
+        const events = generateEvents([
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+        ]);
         const props = {
             events: events,
             children: generateTiles(events),
@@ -163,8 +165,8 @@ describe("EventListSummary", function () {
 
     it("renders expanded events if there are less than props.threshold for join and leave", function () {
         const events = generateEvents([
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
         ]);
         const props = {
             events: events,
@@ -184,9 +186,9 @@ describe("EventListSummary", function () {
 
     it("renders collapsed events if events.length = props.threshold", function () {
         const events = generateEvents([
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
         ]);
         const props = {
             events: events,
@@ -203,20 +205,20 @@ describe("EventListSummary", function () {
 
     it("truncates long join,leave repetitions", function () {
         const events = generateEvents([
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
         ]);
         const props = {
             events: events,
@@ -235,28 +237,28 @@ describe("EventListSummary", function () {
         const events = generateEvents([
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "ban",
-                membership: "leave",
+                prevMembership: Membership.Ban,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "leave",
-                membership: "invite",
+                prevMembership: Membership.Leave,
+                membership: Membership.Invite,
                 senderId: "@some_other_user:some.domain",
             },
         ]);
@@ -277,30 +279,30 @@ describe("EventListSummary", function () {
         const events = generateEvents([
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "ban",
-                membership: "leave",
+                prevMembership: Membership.Ban,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "leave",
-                membership: "ban",
+                prevMembership: Membership.Leave,
+                membership: Membership.Ban,
                 senderId: "@some_other_user:some.domain",
             },
-            { userId: "@user_1:some.domain", prevMembership: "ban", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Ban, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "leave",
-                membership: "invite",
+                prevMembership: Membership.Leave,
+                membership: Membership.Invite,
                 senderId: "@some_other_user:some.domain",
             },
         ]);
@@ -324,35 +326,35 @@ describe("EventListSummary", function () {
             // user_1
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "ban",
-                membership: "leave",
+                prevMembership: Membership.Ban,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "leave",
-                membership: "ban",
+                prevMembership: Membership.Leave,
+                membership: Membership.Ban,
                 senderId: "@some_other_user:some.domain",
             },
             // user_2
             {
                 userId: "@user_2:some.domain",
-                prevMembership: "ban",
-                membership: "leave",
+                prevMembership: Membership.Ban,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
-            { userId: "@user_2:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_2:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_2:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_2:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_2:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_2:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_2:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_2:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
             {
                 userId: "@user_2:some.domain",
-                prevMembership: "leave",
-                membership: "ban",
+                prevMembership: Membership.Leave,
+                membership: Membership.Ban,
                 senderId: "@some_other_user:some.domain",
             },
         ]);
@@ -374,17 +376,17 @@ describe("EventListSummary", function () {
     it("handles many users following the same sequence of memberships", function () {
         const events = generateEventsForUsers("@user_$:some.domain", 20, [
             {
-                prevMembership: "ban",
-                membership: "leave",
+                prevMembership: Membership.Ban,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
-            { prevMembership: "leave", membership: "join" },
-            { prevMembership: "join", membership: "leave" },
-            { prevMembership: "leave", membership: "join" },
-            { prevMembership: "join", membership: "leave" },
+            { prevMembership: Membership.Leave, membership: Membership.Join },
+            { prevMembership: Membership.Join, membership: Membership.Leave },
+            { prevMembership: Membership.Leave, membership: Membership.Join },
+            { prevMembership: Membership.Join, membership: Membership.Leave },
             {
-                prevMembership: "leave",
-                membership: "ban",
+                prevMembership: Membership.Leave,
+                membership: Membership.Ban,
                 senderId: "@some_other_user:some.domain",
             },
         ]);
@@ -407,30 +409,30 @@ describe("EventListSummary", function () {
         const events = generateEvents([
             {
                 userId: "@user_2:some.domain",
-                prevMembership: "ban",
-                membership: "leave",
+                prevMembership: Membership.Ban,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "ban",
-                membership: "leave",
+                prevMembership: Membership.Ban,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_1:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "leave",
-                membership: "ban",
+                prevMembership: Membership.Leave,
+                membership: Membership.Ban,
                 senderId: "@some_other_user:some.domain",
             },
-            { userId: "@user_2:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_2:some.domain", prevMembership: "join", membership: "leave" },
-            { userId: "@user_2:some.domain", prevMembership: "leave", membership: "join" },
-            { userId: "@user_2:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_2:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_2:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
+            { userId: "@user_2:some.domain", prevMembership: Membership.Leave, membership: Membership.Join },
+            { userId: "@user_2:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
         ]);
         const props = {
             events: events,
@@ -451,52 +453,52 @@ describe("EventListSummary", function () {
     it("correctly identifies transitions", function () {
         const events = generateEvents([
             // invited
-            { userId: "@user_1:some.domain", membership: "invite" },
+            { userId: "@user_1:some.domain", membership: Membership.Invite },
             // banned
-            { userId: "@user_1:some.domain", membership: "ban" },
+            { userId: "@user_1:some.domain", membership: Membership.Ban },
             // joined
-            { userId: "@user_1:some.domain", membership: "join" },
+            { userId: "@user_1:some.domain", membership: Membership.Join },
             // invite_reject
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "invite",
-                membership: "leave",
+                prevMembership: Membership.Invite,
+                membership: Membership.Leave,
             },
             // left
-            { userId: "@user_1:some.domain", prevMembership: "join", membership: "leave" },
+            { userId: "@user_1:some.domain", prevMembership: Membership.Join, membership: Membership.Leave },
             // invite_withdrawal
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "invite",
-                membership: "leave",
+                prevMembership: Membership.Invite,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
             // unbanned
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "ban",
-                membership: "leave",
+                prevMembership: Membership.Ban,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
             // kicked
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "join",
-                membership: "leave",
+                prevMembership: Membership.Join,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
             // default for sender=target (leave)
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "????",
-                membership: "leave",
+                prevMembership: "????" as Membership,
+                membership: Membership.Leave,
                 senderId: "@user_1:some.domain",
             },
             // default for sender<>target (kicked)
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "????",
-                membership: "leave",
+                prevMembership: "????" as Membership,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
         ]);
@@ -520,24 +522,24 @@ describe("EventListSummary", function () {
         const events = generateEvents([
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "invite",
-                membership: "leave",
+                prevMembership: Membership.Invite,
+                membership: Membership.Leave,
             },
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "invite",
-                membership: "leave",
+                prevMembership: Membership.Invite,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
             {
                 userId: "@user_2:some.domain",
-                prevMembership: "invite",
-                membership: "leave",
+                prevMembership: Membership.Invite,
+                membership: Membership.Leave,
             },
             {
                 userId: "@user_2:some.domain",
-                prevMembership: "invite",
-                membership: "leave",
+                prevMembership: Membership.Invite,
+                membership: Membership.Leave,
                 senderId: "@some_other_user:some.domain",
             },
         ]);
@@ -560,13 +562,13 @@ describe("EventListSummary", function () {
         const events = generateEvents([
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "invite",
-                membership: "leave",
+                prevMembership: Membership.Invite,
+                membership: Membership.Leave,
             },
             {
                 userId: "@user_1:some.domain",
-                prevMembership: "invite",
-                membership: "leave",
+                prevMembership: Membership.Invite,
+                membership: Membership.Leave,
             },
         ]);
         const props = {
@@ -584,10 +586,10 @@ describe("EventListSummary", function () {
 
     it('handles a summary length = 2, with no "others"', function () {
         const events = generateEvents([
-            { userId: "@user_1:some.domain", membership: "join" },
-            { userId: "@user_1:some.domain", membership: "join" },
-            { userId: "@user_2:some.domain", membership: "join" },
-            { userId: "@user_2:some.domain", membership: "join" },
+            { userId: "@user_1:some.domain", membership: Membership.Join },
+            { userId: "@user_1:some.domain", membership: Membership.Join },
+            { userId: "@user_2:some.domain", membership: Membership.Join },
+            { userId: "@user_2:some.domain", membership: Membership.Join },
         ]);
         const props = {
             events: events,
@@ -604,9 +606,9 @@ describe("EventListSummary", function () {
 
     it('handles a summary length = 2, with 1 "other"', function () {
         const events = generateEvents([
-            { userId: "@user_1:some.domain", membership: "join" },
-            { userId: "@user_2:some.domain", membership: "join" },
-            { userId: "@user_3:some.domain", membership: "join" },
+            { userId: "@user_1:some.domain", membership: Membership.Join },
+            { userId: "@user_2:some.domain", membership: Membership.Join },
+            { userId: "@user_3:some.domain", membership: Membership.Join },
         ]);
         const props = {
             events: events,
@@ -622,7 +624,7 @@ describe("EventListSummary", function () {
     });
 
     it('handles a summary length = 2, with many "others"', function () {
-        const events = generateEventsForUsers("@user_$:some.domain", 20, [{ membership: "join" }]);
+        const events = generateEventsForUsers("@user_$:some.domain", 20, [{ membership: Membership.Join }]);
         const props = {
             events: events,
             children: generateTiles(events),
diff --git a/test/components/views/elements/FacePile-test.tsx b/test/components/views/elements/FacePile-test.tsx
index 3e002145ae..9825c78ddc 100644
--- a/test/components/views/elements/FacePile-test.tsx
+++ b/test/components/views/elements/FacePile-test.tsx
@@ -21,7 +21,7 @@ import { mkRoomMember } from "../../../test-utils";
 
 describe("<FacePile />", () => {
     it("renders with a tooltip", () => {
-        const member = mkRoomMember("123", "456", "join");
+        const member = mkRoomMember("123", "456", Membership.Join);
 
         const { asFragment } = render(
             <FacePile members={[member]} size="36px" overflow={false} tooltipLabel="tooltip" />,
diff --git a/test/components/views/elements/RoomFacePile-test.tsx b/test/components/views/elements/RoomFacePile-test.tsx
index aa75e92d21..e0358f76a5 100644
--- a/test/components/views/elements/RoomFacePile-test.tsx
+++ b/test/components/views/elements/RoomFacePile-test.tsx
@@ -27,7 +27,9 @@ describe("<RoomFacePile />", () => {
         DMRoomMap.makeShared(cli);
         const room = mkRoom(cli, "!123");
 
-        jest.spyOn(room, "getJoinedMembers").mockReturnValue([mkRoomMember(room.roomId, "@bob:example.org", "join")]);
+        jest.spyOn(room, "getJoinedMembers").mockReturnValue([
+            mkRoomMember(room.roomId, "@bob:example.org", Membership.Join),
+        ]);
 
         const { asFragment } = render(
             <TooltipProvider>
diff --git a/test/components/views/right_panel/RoomSummaryCard-test.tsx b/test/components/views/right_panel/RoomSummaryCard-test.tsx
index f26c4e6037..57683c8bb6 100644
--- a/test/components/views/right_panel/RoomSummaryCard-test.tsx
+++ b/test/components/views/right_panel/RoomSummaryCard-test.tsx
@@ -86,7 +86,7 @@ describe("<RoomSummaryCard />", () => {
             state_key: "",
         });
         room.currentState.setStateEvents([roomCreateEvent]);
-        room.updateMyMembership("join");
+        room.updateMyMembership(Membership.Join);
 
         jest.spyOn(Modal, "createDialog");
         jest.spyOn(RightPanelStore.instance, "pushCard");
diff --git a/test/components/views/right_panel/UserInfo-test.tsx b/test/components/views/right_panel/UserInfo-test.tsx
index 4be9c4b6a9..aed084d16b 100644
--- a/test/components/views/right_panel/UserInfo-test.tsx
+++ b/test/components/views/right_panel/UserInfo-test.tsx
@@ -943,8 +943,8 @@ describe("<PowerLevelEditor />", () => {
 
 describe("<RoomKickButton />", () => {
     const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
-    const memberWithInviteMembership = { ...defaultMember, membership: "invite" };
-    const memberWithJoinMembership = { ...defaultMember, membership: "join" };
+    const memberWithInviteMembership = { ...defaultMember, membership: Membership.Invite };
+    const memberWithJoinMembership = { ...defaultMember, membership: Membership.Join };
 
     let defaultProps: Parameters<typeof RoomKickButton>[0];
     beforeEach(() => {
@@ -1031,7 +1031,7 @@ describe("<RoomKickButton />", () => {
         // null vs their member followed by
         // my member vs their member
         const mockMyMember = { powerLevel: 1 };
-        const mockTheirMember = { membership: "invite", powerLevel: 0 };
+        const mockTheirMember = { membership: Membership.Invite, powerLevel: 0 };
 
         const mockRoom = {
             getMember: jest
@@ -1052,7 +1052,7 @@ describe("<RoomKickButton />", () => {
 
 describe("<BanToggleButton />", () => {
     const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
-    const memberWithBanMembership = { ...defaultMember, membership: "ban" };
+    const memberWithBanMembership = { ...defaultMember, membership: Membership.Ban };
     let defaultProps: Parameters<typeof BanToggleButton>[0];
     beforeEach(() => {
         defaultProps = {
@@ -1161,7 +1161,7 @@ describe("<BanToggleButton />", () => {
         // null vs their member followed by
         // my member vs their member
         const mockMyMember = { powerLevel: 1 };
-        const mockTheirMember = { membership: "ban", powerLevel: 0 };
+        const mockTheirMember = { membership: Membership.Ban, powerLevel: 0 };
 
         const mockRoom = {
             getMember: jest
@@ -1182,7 +1182,7 @@ describe("<BanToggleButton />", () => {
 
 describe("<RoomAdminToolsContainer />", () => {
     const defaultMember = new RoomMember(defaultRoomId, defaultUserId);
-    defaultMember.membership = "invite";
+    defaultMember.membership = Membership.Invite;
 
     let defaultProps: Parameters<typeof RoomAdminToolsContainer>[0];
     beforeEach(() => {
@@ -1245,7 +1245,11 @@ describe("<RoomAdminToolsContainer />", () => {
         mockMeMember.powerLevel = 51; // defaults to 50
         mockRoom.getMember.mockReturnValueOnce(mockMeMember);
 
-        const defaultMemberWithPowerLevelAndJoinMembership = { ...defaultMember, powerLevel: 0, membership: "join" };
+        const defaultMemberWithPowerLevelAndJoinMembership = {
+            ...defaultMember,
+            powerLevel: 0,
+            membership: Membership.Join,
+        };
 
         renderComponent({
             member: defaultMemberWithPowerLevelAndJoinMembership,
@@ -1263,7 +1267,11 @@ describe("<RoomAdminToolsContainer />", () => {
         mockMeMember.powerLevel = 51; // defaults to 50
         mockRoom.getMember.mockReturnValueOnce(mockMeMember);
 
-        const defaultMemberWithPowerLevelAndJoinMembership = { ...defaultMember, powerLevel: 0, membership: "join" };
+        const defaultMemberWithPowerLevelAndJoinMembership = {
+            ...defaultMember,
+            powerLevel: 0,
+            membership: Membership.Join,
+        };
 
         renderComponent({
             member: defaultMemberWithPowerLevelAndJoinMembership,
diff --git a/test/components/views/rooms/LegacyRoomHeader-test.tsx b/test/components/views/rooms/LegacyRoomHeader-test.tsx
index 067059801a..5244446b61 100644
--- a/test/components/views/rooms/LegacyRoomHeader-test.tsx
+++ b/test/components/views/rooms/LegacyRoomHeader-test.tsx
@@ -906,7 +906,7 @@ function mkJoinEvent(roomId: string, userId: string) {
         room: roomId,
         user: userId,
         content: {
-            membership: "join",
+            membership: Membership.Join,
             avatar_url: "mxc://example.org/" + userId,
         },
     });
diff --git a/test/components/views/rooms/MemberList-test.tsx b/test/components/views/rooms/MemberList-test.tsx
index fe8f5e3314..7a30e84dde 100644
--- a/test/components/views/rooms/MemberList-test.tsx
+++ b/test/components/views/rooms/MemberList-test.tsx
@@ -170,7 +170,7 @@ describe("MemberList", () => {
         const usersPerLevel = 2;
         for (let i = 0; i < usersPerLevel; i++) {
             const adminUser = new RoomMember(memberListRoom.roomId, `@admin${i}:localhost`);
-            adminUser.membership = "join";
+            adminUser.membership = Membership.Join;
             adminUser.powerLevel = 100;
             adminUser.user = User.createUser(adminUser.userId, client);
             adminUser.user.currentlyActive = true;
@@ -180,7 +180,7 @@ describe("MemberList", () => {
             adminUsers.push(adminUser);
 
             const moderatorUser = new RoomMember(memberListRoom.roomId, `@moderator${i}:localhost`);
-            moderatorUser.membership = "join";
+            moderatorUser.membership = Membership.Join;
             moderatorUser.powerLevel = 50;
             moderatorUser.user = User.createUser(moderatorUser.userId, client);
             moderatorUser.user.currentlyActive = true;
@@ -190,7 +190,7 @@ describe("MemberList", () => {
             moderatorUsers.push(moderatorUser);
 
             const defaultUser = new RoomMember(memberListRoom.roomId, `@default${i}:localhost`);
-            defaultUser.membership = "join";
+            defaultUser.membership = Membership.Join;
             defaultUser.powerLevel = 0;
             defaultUser.user = User.createUser(defaultUser.userId, client);
             defaultUser.user.currentlyActive = true;
@@ -402,7 +402,7 @@ describe("MemberList", () => {
             });
 
             it("renders disabled invite button when current user is a member but does not have rights to invite", async () => {
-                jest.spyOn(room, "getMyMembership").mockReturnValue("join");
+                jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Join);
                 jest.spyOn(room, "canInvite").mockReturnValue(false);
 
                 renderComponent();
@@ -413,7 +413,7 @@ describe("MemberList", () => {
             });
 
             it("renders enabled invite button when current user is a member and has rights to invite", async () => {
-                jest.spyOn(room, "getMyMembership").mockReturnValue("join");
+                jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Join);
                 jest.spyOn(room, "canInvite").mockReturnValue(true);
 
                 renderComponent();
@@ -424,7 +424,7 @@ describe("MemberList", () => {
 
             it("opens room inviter on button click", async () => {
                 jest.spyOn(defaultDispatcher, "dispatch");
-                jest.spyOn(room, "getMyMembership").mockReturnValue("join");
+                jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Join);
                 jest.spyOn(room, "canInvite").mockReturnValue(true);
 
                 renderComponent();
diff --git a/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx b/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx
index 8f884eeb9c..51b3cb29c6 100644
--- a/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx
+++ b/test/components/views/rooms/NotificationBadge/UnreadNotificationBadge-test.tsx
@@ -140,7 +140,7 @@ describe("UnreadNotificationBadge", () => {
     });
 
     it("adds a warning for invites", () => {
-        room.updateMyMembership("invite");
+        room.updateMyMembership(Membership.Invite);
         render(getComponent());
         expect(screen.queryByText("!")).not.toBeNull();
     });
diff --git a/test/components/views/rooms/RoomHeader-test.tsx b/test/components/views/rooms/RoomHeader-test.tsx
index 427b68634a..3a874f9301 100644
--- a/test/components/views/rooms/RoomHeader-test.tsx
+++ b/test/components/views/rooms/RoomHeader-test.tsx
@@ -154,7 +154,7 @@ describe("RoomHeader", () => {
                 name: "Member",
                 rawDisplayName: "Member",
                 roomId: room.roomId,
-                membership: "join",
+                membership: Membership.Join,
                 getAvatarUrl: () => "mxc://avatar.url/image.png",
                 getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
             },
@@ -172,7 +172,7 @@ describe("RoomHeader", () => {
                 name: "Member",
                 rawDisplayName: "Member",
                 roomId: room.roomId,
-                membership: "join",
+                membership: Membership.Join,
                 getAvatarUrl: () => "mxc://avatar.url/image.png",
                 getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
             },
@@ -181,7 +181,7 @@ describe("RoomHeader", () => {
                 name: "Member",
                 rawDisplayName: "Member",
                 roomId: room.roomId,
-                membership: "join",
+                membership: Membership.Join,
                 getAvatarUrl: () => "mxc://avatar.url/image.png",
                 getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
             },
@@ -190,7 +190,7 @@ describe("RoomHeader", () => {
                 name: "Member",
                 rawDisplayName: "Member",
                 roomId: room.roomId,
-                membership: "join",
+                membership: Membership.Join,
                 getAvatarUrl: () => "mxc://avatar.url/image.png",
                 getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
             },
@@ -199,7 +199,7 @@ describe("RoomHeader", () => {
                 name: "Bot user",
                 rawDisplayName: "Bot user",
                 roomId: room.roomId,
-                membership: "join",
+                membership: Membership.Join,
                 getAvatarUrl: () => "mxc://avatar.url/image.png",
                 getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
             },
@@ -703,7 +703,7 @@ function mockRoomMembers(room: Room, count: number) {
             name: `Member ${index}`,
             rawDisplayName: `Member ${index}`,
             roomId: room.roomId,
-            membership: "join",
+            membership: Membership.Join,
             getAvatarUrl: () => `mxc://avatar.url/user-${index}.png`,
             getMxcAvatarUrl: () => `mxc://avatar.url/user-${index}.png`,
         }));
diff --git a/test/components/views/rooms/RoomKnocksBar-test.tsx b/test/components/views/rooms/RoomKnocksBar-test.tsx
index 89a1863cbc..a96b760033 100644
--- a/test/components/views/rooms/RoomKnocksBar-test.tsx
+++ b/test/components/views/rooms/RoomKnocksBar-test.tsx
@@ -111,15 +111,26 @@ describe("RoomKnocksBar", () => {
         const other = new RoomMember(roomId, "@doe:example.org");
 
         bob.setMembershipEvent(
-            new MatrixEvent({ content: { displayname: "Bob", membership: "knock" }, type: EventType.RoomMember }),
+            new MatrixEvent({
+                content: { displayname: "Bob", membership: Membership.Knock },
+                type: EventType.RoomMember,
+            }),
         );
         jane.setMembershipEvent(
-            new MatrixEvent({ content: { displayname: "Jane", membership: "knock" }, type: EventType.RoomMember }),
+            new MatrixEvent({
+                content: { displayname: "Jane", membership: Membership.Knock },
+                type: EventType.RoomMember,
+            }),
         );
         john.setMembershipEvent(
-            new MatrixEvent({ content: { displayname: "John", membership: "knock" }, type: EventType.RoomMember }),
+            new MatrixEvent({
+                content: { displayname: "John", membership: Membership.Knock },
+                type: EventType.RoomMember,
+            }),
+        );
+        other.setMembershipEvent(
+            new MatrixEvent({ content: { membership: Membership.Knock }, type: EventType.RoomMember }),
         );
-        other.setMembershipEvent(new MatrixEvent({ content: { membership: "knock" }, type: EventType.RoomMember }));
 
         beforeEach(async () => {
             await clearAllModals();
@@ -178,7 +189,7 @@ describe("RoomKnocksBar", () => {
                 it("renders a link to open the room settings people tab", () => {
                     bob.setMembershipEvent(
                         new MatrixEvent({
-                            content: { displayname: "Bob", membership: "knock", reason: "some reason" },
+                            content: { displayname: "Bob", membership: Membership.Knock, reason: "some reason" },
                             type: EventType.RoomMember,
                         }),
                     );
diff --git a/test/components/views/rooms/RoomPreviewBar-test.tsx b/test/components/views/rooms/RoomPreviewBar-test.tsx
index 3a52cf290e..a047a80c8d 100644
--- a/test/components/views/rooms/RoomPreviewBar-test.tsx
+++ b/test/components/views/rooms/RoomPreviewBar-test.tsx
@@ -49,10 +49,10 @@ const makeMockRoomMember = ({
 }: {
     userId?: string;
     isKicked?: boolean;
-    membership?: "invite" | "ban" | "leave";
+    membership?: Membership.Invite | Membership.Ban | Membership.Leave;
     content?: Partial<IContent>;
     memberContent?: Partial<IContent>;
-    oldMembership?: "join" | "knock";
+    oldMembership?: Membership.Join | Membership.Knock;
 }) =>
     ({
         userId,
@@ -180,7 +180,7 @@ describe("<RoomPreviewBar />", () => {
     it("renders denied request message", () => {
         const room = createRoom(roomId, otherUserId);
         jest.spyOn(room, "getMember").mockReturnValue(
-            makeMockRoomMember({ isKicked: true, membership: "leave", oldMembership: "knock" }),
+            makeMockRoomMember({ isKicked: true, membership: Membership.Leave, oldMembership: Membership.Knock }),
         );
         const component = getComponent({ room, promptAskToJoin: true });
 
@@ -191,7 +191,7 @@ describe("<RoomPreviewBar />", () => {
         const onForgetClick = jest.fn();
         const room = createRoom(roomId, otherUserId);
         jest.spyOn(room, "getMember").mockReturnValue(
-            makeMockRoomMember({ isKicked: true, membership: "leave", oldMembership: "knock" }),
+            makeMockRoomMember({ isKicked: true, membership: Membership.Leave, oldMembership: Membership.Knock }),
         );
         const component = getComponent({ room, promptAskToJoin: true, onForgetClick });
 
@@ -201,7 +201,7 @@ describe("<RoomPreviewBar />", () => {
 
     it("renders banned message", () => {
         const room = createRoom(roomId, otherUserId);
-        jest.spyOn(room, "getMember").mockReturnValue(makeMockRoomMember({ membership: "ban" }));
+        jest.spyOn(room, "getMember").mockReturnValue(makeMockRoomMember({ membership: Membership.Ban }));
         const component = getComponent({ loading: true, room });
 
         expect(getMessage(component)).toMatchSnapshot();
@@ -244,8 +244,8 @@ describe("<RoomPreviewBar />", () => {
         const userMember = makeMockRoomMember({ userId });
         const userMemberWithDmInvite = makeMockRoomMember({
             userId,
-            membership: "invite",
-            memberContent: { is_direct: true, membership: "invite" },
+            membership: Membership.Invite,
+            memberContent: { is_direct: true, membership: Membership.Invite },
         });
         const inviterMember = makeMockRoomMember({
             userId: inviterUserId,
diff --git a/test/components/views/rooms/RoomPreviewCard-test.tsx b/test/components/views/rooms/RoomPreviewCard-test.tsx
index c062497ec8..7422504c15 100644
--- a/test/components/views/rooms/RoomPreviewCard-test.tsx
+++ b/test/components/views/rooms/RoomPreviewCard-test.tsx
@@ -68,7 +68,7 @@ describe("RoomPreviewCard", () => {
 
     it("shows a beta pill on Jitsi video room invites", async () => {
         jest.spyOn(room, "getType").mockReturnValue(RoomType.ElementVideo);
-        jest.spyOn(room, "getMyMembership").mockReturnValue("invite");
+        jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Invite);
         enabledFeatures = ["feature_video_rooms"];
 
         await renderPreview();
@@ -77,7 +77,7 @@ describe("RoomPreviewCard", () => {
 
     it("shows a beta pill on Element video room invites", async () => {
         jest.spyOn(room, "getType").mockReturnValue(RoomType.UnstableCall);
-        jest.spyOn(room, "getMyMembership").mockReturnValue("invite");
+        jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Invite);
         enabledFeatures = ["feature_video_rooms", "feature_element_call_video_rooms"];
 
         await renderPreview();
@@ -85,7 +85,7 @@ describe("RoomPreviewCard", () => {
     });
 
     it("doesn't show a beta pill on normal invites", async () => {
-        jest.spyOn(room, "getMyMembership").mockReturnValue("invite");
+        jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Invite);
 
         await renderPreview();
         expect(screen.queryByRole("button", { name: /beta/i })).toBeNull();
@@ -93,7 +93,7 @@ describe("RoomPreviewCard", () => {
 
     it("shows instructions on Jitsi video rooms invites if video rooms are disabled", async () => {
         jest.spyOn(room, "getType").mockReturnValue(RoomType.ElementVideo);
-        jest.spyOn(room, "getMyMembership").mockReturnValue("invite");
+        jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Invite);
 
         await renderPreview();
         screen.getByText(/enable video rooms in labs/i);
@@ -101,7 +101,7 @@ describe("RoomPreviewCard", () => {
 
     it("shows instructions on Element video rooms invites if video rooms are disabled", async () => {
         jest.spyOn(room, "getType").mockReturnValue(RoomType.UnstableCall);
-        jest.spyOn(room, "getMyMembership").mockReturnValue("invite");
+        jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Invite);
         enabledFeatures = ["feature_element_call_video_rooms"];
 
         await renderPreview();
diff --git a/test/components/views/rooms/RoomTile-test.tsx b/test/components/views/rooms/RoomTile-test.tsx
index 3cf3b3fee5..591f79faf8 100644
--- a/test/components/views/rooms/RoomTile-test.tsx
+++ b/test/components/views/rooms/RoomTile-test.tsx
@@ -187,7 +187,7 @@ describe("RoomTile", () => {
                 return name === "feature_ask_to_join";
             });
             mocked(shouldShowComponent).mockReturnValue(true);
-            jest.spyOn(room, "getMyMembership").mockReturnValue("knock");
+            jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Knock);
             const { container } = renderRoomTile();
             expect(container.querySelector(".mx_RoomTile_sticky")).toBeInTheDocument();
             expect(screen.queryByRole("button", { name: "Room options" })).not.toBeInTheDocument();
@@ -198,9 +198,15 @@ describe("RoomTile", () => {
                 return name === "feature_ask_to_join";
             });
             mocked(shouldShowComponent).mockReturnValue(true);
-            const roomMember = mkRoomMember(room.roomId, MatrixClientPeg.get()!.getSafeUserId(), "leave", true, {
-                membership: "knock",
-            });
+            const roomMember = mkRoomMember(
+                room.roomId,
+                MatrixClientPeg.get()!.getSafeUserId(),
+                Membership.Leave,
+                true,
+                {
+                    membership: Membership.Knock,
+                },
+            );
             jest.spyOn(room, "getMember").mockReturnValue(roomMember);
             const { container } = renderRoomTile();
             expect(container.querySelector(".mx_RoomTile_sticky")).toBeInTheDocument();
diff --git a/test/components/views/settings/JoinRuleSettings-test.tsx b/test/components/views/settings/JoinRuleSettings-test.tsx
index 636c2ffd95..ab8e029d67 100644
--- a/test/components/views/settings/JoinRuleSettings-test.tsx
+++ b/test/components/views/settings/JoinRuleSettings-test.tsx
@@ -177,7 +177,7 @@ describe("<JoinRuleSettings />", () => {
                 const memberBob = new RoomMember(roomId, "@bob:server.org");
                 const memberCharlie = new RoomMember(roomId, "@charlie:server.org");
                 jest.spyOn(room, "getMembersWithMembership").mockImplementation((membership) =>
-                    membership === "join" ? [memberAlice, memberBob] : [memberCharlie],
+                    membership === Membership.Join ? [memberAlice, memberBob] : [memberCharlie],
                 );
                 const upgradedRoom = new Room(newRoomId, client, userId);
                 setRoomStateEvents(upgradedRoom, preferredRoomVersion);
diff --git a/test/components/views/settings/tabs/room/PeopleRoomSettingsTab-test.tsx b/test/components/views/settings/tabs/room/PeopleRoomSettingsTab-test.tsx
index 6c9051c0af..9ddb8b1022 100644
--- a/test/components/views/settings/tabs/room/PeopleRoomSettingsTab-test.tsx
+++ b/test/components/views/settings/tabs/room/PeopleRoomSettingsTab-test.tsx
@@ -91,7 +91,7 @@ describe("PeopleRoomSettingsTab", () => {
                     content: {
                         avatar_url: "mxc://example.org/albert-einstein.png",
                         displayname: "Albert Einstein",
-                        membership: "knock",
+                        membership: Membership.Knock,
                         reason,
                     },
                     origin_server_ts: -464140800000,
@@ -110,7 +110,7 @@ describe("PeopleRoomSettingsTab", () => {
                 new MatrixEvent({
                     content: {
                         displayname: "albert.einstein",
-                        membership: "knock",
+                        membership: Membership.Knock,
                     },
                     type: EventType.RoomMember,
                 }),
@@ -138,7 +138,7 @@ describe("PeopleRoomSettingsTab", () => {
                 new MatrixEvent({
                     content: {
                         displayname: "albert.einstein",
-                        membership: "knock",
+                        membership: Membership.Knock,
                         reason,
                     },
                     type: EventType.RoomMember,
diff --git a/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx b/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx
index f121f6f045..643d24b391 100644
--- a/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx
+++ b/test/components/views/settings/tabs/room/RolesRoomSettingsTab-test.tsx
@@ -197,7 +197,7 @@ describe("RolesRoomSettingsTab", () => {
                 new MatrixEvent({
                     type: EventType.RoomMember,
                     content: {
-                        membership: "ban",
+                        membership: Membership.Ban,
                         reason: "just testing",
                     },
                     sender: userId,
@@ -218,7 +218,7 @@ describe("RolesRoomSettingsTab", () => {
                 new MatrixEvent({
                     type: EventType.RoomMember,
                     content: {
-                        membership: "ban",
+                        membership: Membership.Ban,
                         reason: "just testing",
                     },
                     sender: userId,
diff --git a/test/hooks/useRoomMembers-test.tsx b/test/hooks/useRoomMembers-test.tsx
index 934f318391..980bb14dd1 100644
--- a/test/hooks/useRoomMembers-test.tsx
+++ b/test/hooks/useRoomMembers-test.tsx
@@ -49,7 +49,7 @@ describe("useRoomMembers", () => {
                     state_key: "!user:server",
                     room_id: room.roomId,
                     content: {
-                        membership: "join",
+                        membership: Membership.Join,
                     },
                 }),
             ]);
@@ -85,7 +85,7 @@ describe("useRoomMemberCount", () => {
                     state_key: "!user:server",
                     room_id: room.roomId,
                     content: {
-                        membership: "join",
+                        membership: Membership.Join,
                     },
                 }),
             ]);
@@ -109,14 +109,14 @@ describe("useMyRoomMembership", () => {
     });
 
     it("should update on RoomState.Members events", async () => {
-        room.updateMyMembership("join");
+        room.updateMyMembership(Membership.Join);
         const { result } = render(room);
 
-        expect(result.current).toBe("join");
+        expect(result.current).toBe(Membership.Join);
 
         act(() => {
-            room.updateMyMembership("leave");
+            room.updateMyMembership(Membership.Leave);
         });
-        await waitFor(() => expect(result.current).toBe("leave"));
+        await waitFor(() => expect(result.current).toBe(Membership.Leave));
     });
 });
diff --git a/test/hooks/useUnreadNotifications-test.ts b/test/hooks/useUnreadNotifications-test.ts
index 504dbfddb1..6a7114995d 100644
--- a/test/hooks/useUnreadNotifications-test.ts
+++ b/test/hooks/useUnreadNotifications-test.ts
@@ -66,7 +66,7 @@ describe("useUnreadNotifications", () => {
     });
 
     it("indicates the user has been invited to a channel", async () => {
-        room.updateMyMembership("invite");
+        room.updateMyMembership(Membership.Invite);
 
         const { result } = renderHook(() => useUnreadNotifications(room));
         const { level, symbol, count } = result.current;
diff --git a/test/models/Call-test.ts b/test/models/Call-test.ts
index 1ebdfff5a5..a5031be52a 100644
--- a/test/models/Call-test.ts
+++ b/test/models/Call-test.ts
@@ -95,7 +95,7 @@ const setUpClientRoomAndStores = (): {
         }
     });
 
-    jest.spyOn(room, "getMyMembership").mockReturnValue("join");
+    jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Join);
 
     client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
     client.getRoom.mockImplementation((roomId) => (roomId === room.roomId ? room : null));
@@ -380,7 +380,7 @@ describe("JitsiCall", () => {
         it("disconnects when we leave the room", async () => {
             await call.start();
             expect(call.connectionState).toBe(ConnectionState.Connected);
-            room.emit(RoomEvent.MyMembership, room, "leave");
+            room.emit(RoomEvent.MyMembership, room, Membership.Leave);
             expect(call.connectionState).toBe(ConnectionState.Disconnected);
         });
 
@@ -395,7 +395,7 @@ describe("JitsiCall", () => {
         it("remains connected if we stay in the room", async () => {
             await call.start();
             expect(call.connectionState).toBe(ConnectionState.Connected);
-            room.emit(RoomEvent.MyMembership, room, "join");
+            room.emit(RoomEvent.MyMembership, room, Membership.Join);
             expect(call.connectionState).toBe(ConnectionState.Connected);
         });
 
@@ -911,14 +911,14 @@ describe("ElementCall", () => {
         it("disconnects when we leave the room", async () => {
             await callConnectProcedure(call);
             expect(call.connectionState).toBe(ConnectionState.Connected);
-            room.emit(RoomEvent.MyMembership, room, "leave");
+            room.emit(RoomEvent.MyMembership, room, Membership.Leave);
             expect(call.connectionState).toBe(ConnectionState.Disconnected);
         });
 
         it("remains connected if we stay in the room", async () => {
             await callConnectProcedure(call);
             expect(call.connectionState).toBe(ConnectionState.Connected);
-            room.emit(RoomEvent.MyMembership, room, "join");
+            room.emit(RoomEvent.MyMembership, room, Membership.Join);
             expect(call.connectionState).toBe(ConnectionState.Connected);
         });
 
diff --git a/test/stores/MemberListStore-test.ts b/test/stores/MemberListStore-test.ts
index a97b00ba0f..97b8dfd3b0 100644
--- a/test/stores/MemberListStore-test.ts
+++ b/test/stores/MemberListStore-test.ts
@@ -55,7 +55,7 @@ describe("MemberListStore", () => {
                 type: EventType.RoomMember,
                 state_key: alice,
                 content: {
-                    membership: "join",
+                    membership: Membership.Join,
                 },
                 sender: alice,
                 room_id: roomId,
@@ -77,8 +77,8 @@ describe("MemberListStore", () => {
     });
 
     it("loads members in a room", async () => {
-        addMember(room, bob, "invite");
-        addMember(room, charlie, "leave");
+        addMember(room, bob, Membership.Invite);
+        addMember(room, charlie, Membership.Leave);
 
         const { invited, joined } = await store.loadMemberList(roomId);
         expect(invited).toEqual([room.getMember(bob)]);
@@ -92,8 +92,8 @@ describe("MemberListStore", () => {
     });
 
     it("sorts by power level", async () => {
-        addMember(room, bob, "join");
-        addMember(room, charlie, "join");
+        addMember(room, bob, Membership.Join);
+        addMember(room, charlie, Membership.Join);
         setPowerLevels(room, {
             users: {
                 [alice]: 100,
@@ -109,8 +109,8 @@ describe("MemberListStore", () => {
 
     it("sorts by name if power level is equal", async () => {
         const doris = "@doris:bar";
-        addMember(room, bob, "join");
-        addMember(room, charlie, "join");
+        addMember(room, bob, Membership.Join);
+        addMember(room, charlie, Membership.Join);
         setPowerLevels(room, {
             users_default: 10,
         });
@@ -120,7 +120,7 @@ describe("MemberListStore", () => {
         expect(joined).toEqual([room.getMember(alice), room.getMember(bob), room.getMember(charlie)]);
 
         // Ensure it sorts by display name if they are set
-        addMember(room, doris, "join", "AAAAA");
+        addMember(room, doris, Membership.Join, "AAAAA");
         ({ invited, joined } = await store.loadMemberList(roomId));
         expect(invited).toEqual([]);
         expect(joined).toEqual([
@@ -134,15 +134,15 @@ describe("MemberListStore", () => {
     it("filters based on a search query", async () => {
         const mice = "@mice:bar";
         const zorro = "@zorro:bar";
-        addMember(room, bob, "join");
-        addMember(room, mice, "join");
+        addMember(room, bob, Membership.Join);
+        addMember(room, mice, Membership.Join);
 
         let { invited, joined } = await store.loadMemberList(roomId, "ice");
         expect(invited).toEqual([]);
         expect(joined).toEqual([room.getMember(alice), room.getMember(mice)]);
 
         // Ensure it filters by display name if they are set
-        addMember(room, zorro, "join", "ice ice baby");
+        addMember(room, zorro, Membership.Join, "ice ice baby");
         ({ invited, joined } = await store.loadMemberList(roomId, "ice"));
         expect(invited).toEqual([]);
         expect(joined).toEqual([room.getMember(alice), room.getMember(zorro), room.getMember(mice)]);
@@ -180,7 +180,7 @@ describe("MemberListStore", () => {
                         type: EventType.RoomMember,
                         state_key: bob,
                         content: {
-                            membership: "join",
+                            membership: Membership.Join,
                             displayname: "Bob",
                         },
                         sender: bob,
diff --git a/test/stores/OwnBeaconStore-test.ts b/test/stores/OwnBeaconStore-test.ts
index 378ebfe94c..57f3bc668b 100644
--- a/test/stores/OwnBeaconStore-test.ts
+++ b/test/stores/OwnBeaconStore-test.ts
@@ -556,7 +556,7 @@ describe("OwnBeaconStore", () => {
 
         it("destroys and removes beacons when current user leaves room", async () => {
             // alice leaves room1
-            const membershipEvent = makeMembershipEvent(room1Id, aliceId, "leave");
+            const membershipEvent = makeMembershipEvent(room1Id, aliceId, Membership.Leave);
             const member = new RoomMember(room1Id, aliceId);
             member.setMembershipEvent(membershipEvent);
 
diff --git a/test/stores/SpaceStore-test.ts b/test/stores/SpaceStore-test.ts
index f13174066d..e20fa4e64f 100644
--- a/test/stores/SpaceStore-test.ts
+++ b/test/stores/SpaceStore-test.ts
@@ -57,13 +57,13 @@ const fav2 = "!fav2:server";
 const fav3 = "!fav3:server";
 const dm1 = "!dm1:server";
 const dm1Partner = new RoomMember(dm1, "@dm1Partner:server");
-dm1Partner.membership = "join";
+dm1Partner.membership = Membership.Join;
 const dm2 = "!dm2:server";
 const dm2Partner = new RoomMember(dm2, "@dm2Partner:server");
-dm2Partner.membership = "join";
+dm2Partner.membership = Membership.Join;
 const dm3 = "!dm3:server";
 const dm3Partner = new RoomMember(dm3, "@dm3Partner:server");
-dm3Partner.membership = "join";
+dm3Partner.membership = Membership.Join;
 const orphan1 = "!orphan1:server";
 const orphan2 = "!orphan2:server";
 const invite1 = "!invite1:server";
@@ -289,7 +289,7 @@ describe("SpaceStore", () => {
         });
 
         it("invite to a subspace is only shown at the top level", async () => {
-            mkSpace(invite1).getMyMembership.mockReturnValue("invite");
+            mkSpace(invite1).getMyMembership.mockReturnValue(Membership.Invite);
             mkSpace(space1, [invite1]);
             await run();
 
@@ -335,20 +335,20 @@ describe("SpaceStore", () => {
                 });
 
                 [invite1, invite2].forEach((roomId) => {
-                    mocked(client.getRoom(roomId)!).getMyMembership.mockReturnValue("invite");
+                    mocked(client.getRoom(roomId)).getMyMembership.mockReturnValue(Membership.Invite);
                 });
 
                 // have dmPartner1 be in space1 with you
                 const mySpace1Member = new RoomMember(space1, testUserId);
-                mySpace1Member.membership = "join";
-                (rooms.find((r) => r.roomId === space1)!.getMembers as jest.Mock).mockReturnValue([
+                mySpace1Member.membership = Membership.Join;
+                (rooms.find((r) => r.roomId === space1).getMembers as jest.Mock).mockReturnValue([
                     mySpace1Member,
                     dm1Partner,
                 ]);
                 // have dmPartner2 be in space2 with you
                 const mySpace2Member = new RoomMember(space2, testUserId);
-                mySpace2Member.membership = "join";
-                (rooms.find((r) => r.roomId === space2)!.getMembers as jest.Mock).mockReturnValue([
+                mySpace2Member.membership = Membership.Join;
+                (rooms.find((r) => r.roomId === space2).getMembers as jest.Mock).mockReturnValue([
                     mySpace2Member,
                     dm2Partner,
                 ]);
@@ -683,16 +683,16 @@ describe("SpaceStore", () => {
 
     it("should add new DM Invites to the People Space Notification State", async () => {
         mkRoom(dm1);
-        mocked(client.getRoom(dm1)!).getMyMembership.mockReturnValue("join");
+        mocked(client.getRoom(dm1)!).getMyMembership.mockReturnValue(Membership.Join);
         mocked(client).getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId) || null);
 
         await run();
 
         mkRoom(dm2);
         const cliDm2 = client.getRoom(dm2)!;
-        mocked(cliDm2).getMyMembership.mockReturnValue("invite");
+        mocked(cliDm2).getMyMembership.mockReturnValue(Membership.Invite);
         mocked(client).getRoom.mockImplementation((roomId) => rooms.find((room) => room.roomId === roomId) || null);
-        client.emit(RoomEvent.MyMembership, cliDm2, "invite");
+        client.emit(RoomEvent.MyMembership, cliDm2, Membership.Invite);
 
         [dm1, dm2].forEach((d) => {
             expect(
@@ -721,9 +721,9 @@ describe("SpaceStore", () => {
             await run();
 
             expect(store.spacePanelSpaces).toStrictEqual([space]);
-            space.getMyMembership.mockReturnValue("leave");
+            space.getMyMembership.mockReturnValue(Membership.Leave);
             const prom = testUtils.emitPromise(store, UPDATE_TOP_LEVEL_SPACES);
-            client.emit(RoomEvent.MyMembership, space, "leave", "join");
+            client.emit(RoomEvent.MyMembership, space, Membership.Leave, Membership.Join);
             await prom;
             expect(store.spacePanelSpaces).toStrictEqual([]);
         });
@@ -733,7 +733,7 @@ describe("SpaceStore", () => {
             expect(store.spacePanelSpaces).toStrictEqual([]);
             expect(store.invitedSpaces).toStrictEqual([]);
             const space = mkSpace(space1);
-            space.getMyMembership.mockReturnValue("invite");
+            space.getMyMembership.mockReturnValue(Membership.Invite);
             const prom = testUtils.emitPromise(store, UPDATE_INVITED_SPACES);
             client.emit(ClientEvent.Room, space);
             await prom;
@@ -743,14 +743,14 @@ describe("SpaceStore", () => {
 
         it("updates state when space invite is accepted", async () => {
             const space = mkSpace(space1);
-            space.getMyMembership.mockReturnValue("invite");
+            space.getMyMembership.mockReturnValue(Membership.Invite);
             await run();
 
             expect(store.spacePanelSpaces).toStrictEqual([]);
             expect(store.invitedSpaces).toStrictEqual([space]);
-            space.getMyMembership.mockReturnValue("join");
+            space.getMyMembership.mockReturnValue(Membership.Join);
             const prom = testUtils.emitPromise(store, UPDATE_TOP_LEVEL_SPACES);
-            client.emit(RoomEvent.MyMembership, space, "join", "invite");
+            client.emit(RoomEvent.MyMembership, space, Membership.Join, Membership.Invite);
             await prom;
             expect(store.spacePanelSpaces).toStrictEqual([space]);
             expect(store.invitedSpaces).toStrictEqual([]);
@@ -758,14 +758,14 @@ describe("SpaceStore", () => {
 
         it("updates state when space invite is rejected", async () => {
             const space = mkSpace(space1);
-            space.getMyMembership.mockReturnValue("invite");
+            space.getMyMembership.mockReturnValue(Membership.Invite);
             await run();
 
             expect(store.spacePanelSpaces).toStrictEqual([]);
             expect(store.invitedSpaces).toStrictEqual([space]);
-            space.getMyMembership.mockReturnValue("leave");
+            space.getMyMembership.mockReturnValue(Membership.Leave);
             const prom = testUtils.emitPromise(store, UPDATE_INVITED_SPACES);
-            client.emit(RoomEvent.MyMembership, space, "leave", "invite");
+            client.emit(RoomEvent.MyMembership, space, Membership.Leave, Membership.Invite);
             await prom;
             expect(store.spacePanelSpaces).toStrictEqual([]);
             expect(store.invitedSpaces).toStrictEqual([]);
@@ -783,7 +783,7 @@ describe("SpaceStore", () => {
             expect(store.isRoomInSpace(MetaSpace.Home, invite1)).toBeFalsy();
 
             const invite = mkRoom(invite1);
-            invite.getMyMembership.mockReturnValue("invite");
+            invite.getMyMembership.mockReturnValue(Membership.Invite);
             const prom = testUtils.emitPromise(store, space1);
             client.emit(ClientEvent.Room, space);
             await prom;
@@ -845,7 +845,7 @@ describe("SpaceStore", () => {
                     room: spaceId,
                     user: client.getUserId()!,
                     skey: user.userId,
-                    content: { membership: "join" },
+                    content: { membership: Membership.Join },
                     ts: Date.now(),
                 });
                 const spaceRoom = client.getRoom(spaceId)!;
@@ -926,7 +926,7 @@ describe("SpaceStore", () => {
         beforeEach(async () => {
             mkRoom(room1); // not a space
             mkSpace(space1, [mkSpace(space2).roomId]);
-            mkSpace(space3).getMyMembership.mockReturnValue("invite");
+            mkSpace(space3).getMyMembership.mockReturnValue(Membership.Invite);
             await run();
             store.setActiveSpace(MetaSpace.Home);
             expect(store.activeSpace).toBe(MetaSpace.Home);
@@ -986,7 +986,7 @@ describe("SpaceStore", () => {
             const event = mkEvent({
                 event: true,
                 type: EventType.RoomMember,
-                content: { membership: "join" },
+                content: { membership: Membership.Join },
                 skey: dm1Partner.userId,
                 user: dm1Partner.userId,
                 room: space1,
@@ -994,7 +994,7 @@ describe("SpaceStore", () => {
             space.getMember.mockImplementation((userId) => {
                 if (userId === dm1Partner.userId) {
                     const member = new RoomMember(space1, dm1Partner.userId);
-                    member.membership = "join";
+                    member.membership = Membership.Join;
                     return member;
                 }
                 return null;
@@ -1249,15 +1249,15 @@ describe("SpaceStore", () => {
 
         // receive invite to space
         const rootSpace = mkSpace(space1, [room1, room2, space2]);
-        rootSpace.getMyMembership.mockReturnValue("invite");
+        rootSpace.getMyMembership.mockReturnValue(Membership.Invite);
         client.emit(ClientEvent.Room, rootSpace);
         jest.runOnlyPendingTimers();
         expect(SpaceStore.instance.invitedSpaces).toStrictEqual([rootSpace]);
         expect(SpaceStore.instance.spacePanelSpaces).toStrictEqual([]);
 
         // accept invite to space
-        rootSpace.getMyMembership.mockReturnValue("join");
-        client.emit(RoomEvent.MyMembership, rootSpace, "join", "invite");
+        rootSpace.getMyMembership.mockReturnValue(Membership.Join);
+        client.emit(RoomEvent.MyMembership, rootSpace, Membership.Join, Membership.Invite);
         jest.runOnlyPendingTimers();
         expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
         expect(SpaceStore.instance.spacePanelSpaces).toStrictEqual([rootSpace]);
@@ -1265,7 +1265,7 @@ describe("SpaceStore", () => {
         // join room in space
         expect(SpaceStore.instance.isRoomInSpace(space1, room1)).toBeFalsy();
         const rootSpaceRoom1 = mkRoom(room1);
-        rootSpaceRoom1.getMyMembership.mockReturnValue("join");
+        rootSpaceRoom1.getMyMembership.mockReturnValue(Membership.Join);
         client.emit(ClientEvent.Room, rootSpaceRoom1);
         jest.runOnlyPendingTimers();
         expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
@@ -1279,7 +1279,7 @@ describe("SpaceStore", () => {
         // receive room invite
         expect(SpaceStore.instance.isRoomInSpace(space1, room2)).toBeFalsy();
         const rootSpaceRoom2 = mkRoom(room2);
-        rootSpaceRoom2.getMyMembership.mockReturnValue("invite");
+        rootSpaceRoom2.getMyMembership.mockReturnValue(Membership.Invite);
         client.emit(ClientEvent.Room, rootSpaceRoom2);
         jest.runOnlyPendingTimers();
         expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
@@ -1292,9 +1292,9 @@ describe("SpaceStore", () => {
 
         // start DM in space
         const myRootSpaceMember = new RoomMember(space1, testUserId);
-        myRootSpaceMember.membership = "join";
+        myRootSpaceMember.membership = Membership.Join;
         const rootSpaceFriend = new RoomMember(space1, dm1Partner.userId);
-        rootSpaceFriend.membership = "join";
+        rootSpaceFriend.membership = Membership.Join;
         rootSpace.getMembers.mockReturnValue([myRootSpaceMember, rootSpaceFriend]);
         rootSpace.getMember.mockImplementation((userId) => {
             switch (userId) {
@@ -1310,7 +1310,7 @@ describe("SpaceStore", () => {
             event: true,
             type: EventType.RoomMember,
             content: {
-                membership: "join",
+                membership: Membership.Join,
             },
             skey: dm1Partner.userId,
             user: dm1Partner.userId,
@@ -1320,7 +1320,7 @@ describe("SpaceStore", () => {
         jest.runOnlyPendingTimers();
         expect(SpaceStore.instance.getSpaceFilteredUserIds(space1)!.has(dm1Partner.userId)).toBeTruthy();
         const dm1Room = mkRoom(dm1);
-        dm1Room.getMyMembership.mockReturnValue("join");
+        dm1Room.getMyMembership.mockReturnValue(Membership.Join);
         client.emit(ClientEvent.Room, dm1Room);
         jest.runOnlyPendingTimers();
         expect(SpaceStore.instance.invitedSpaces).toStrictEqual([]);
@@ -1333,7 +1333,7 @@ describe("SpaceStore", () => {
 
         // join subspace
         const subspace = mkSpace(space2);
-        subspace.getMyMembership.mockReturnValue("join");
+        subspace.getMyMembership.mockReturnValue(Membership.Join);
         const prom = testUtils.emitPromise(SpaceStore.instance, space1);
         client.emit(ClientEvent.Room, subspace);
         jest.runOnlyPendingTimers();
diff --git a/test/stores/notifications/RoomNotificationState-test.ts b/test/stores/notifications/RoomNotificationState-test.ts
index 1e124d1527..e94a84ac04 100644
--- a/test/stores/notifications/RoomNotificationState-test.ts
+++ b/test/stores/notifications/RoomNotificationState-test.ts
@@ -119,7 +119,7 @@ describe("RoomNotificationState", () => {
 
         muteRoom(room);
         setUnreads(room, 1234, 0);
-        room.updateMyMembership("join"); // emit
+        room.updateMyMembership(Membership.Join); // emit
 
         expect(roomNotifState.level).toBe(NotificationLevel.None);
         expect(roomNotifState.symbol).toBe(null);
@@ -129,7 +129,7 @@ describe("RoomNotificationState", () => {
     it("suggests a red ! if the user has been invited to a room", () => {
         const roomNotifState = new RoomNotificationState(room, false);
 
-        room.updateMyMembership("invite"); // emit
+        room.updateMyMembership(Membership.Invite); // emit
 
         expect(roomNotifState.level).toBe(NotificationLevel.Highlight);
         expect(roomNotifState.symbol).toBe("!");
@@ -140,7 +140,7 @@ describe("RoomNotificationState", () => {
         const roomNotifState = new RoomNotificationState(room, false);
 
         setUnreads(room, 4321, 0);
-        room.updateMyMembership("join"); // emit
+        room.updateMyMembership(Membership.Join); // emit
 
         expect(roomNotifState.level).toBe(NotificationLevel.Notification);
         expect(roomNotifState.symbol).toBe(null);
@@ -151,7 +151,7 @@ describe("RoomNotificationState", () => {
         const roomNotifState = new RoomNotificationState(room, false);
 
         setUnreads(room, 0, 69);
-        room.updateMyMembership("join"); // emit
+        room.updateMyMembership(Membership.Join); // emit
 
         expect(roomNotifState.level).toBe(NotificationLevel.Highlight);
         expect(roomNotifState.symbol).toBe(null);
@@ -171,7 +171,7 @@ describe("RoomNotificationState", () => {
         );
 
         addThread(room);
-        room.updateMyMembership("join"); // emit
+        room.updateMyMembership(Membership.Join); // emit
 
         expect(roomNotifState.level).toBe(NotificationLevel.Activity);
         expect(roomNotifState.symbol).toBe(null);
diff --git a/test/stores/room-list/RoomListStore-test.ts b/test/stores/room-list/RoomListStore-test.ts
index 4c00ac11b6..4057cb1336 100644
--- a/test/stores/room-list/RoomListStore-test.ts
+++ b/test/stores/room-list/RoomListStore-test.ts
@@ -123,8 +123,8 @@ describe("RoomListStore", () => {
         // When we tell it we joined a new room that has an old room as
         // predecessor in the create event
         const payload = {
-            oldMembership: "invite",
-            membership: "join",
+            oldMembership: Membership.Invite,
+            membership: Membership.Join,
             room: roomWithCreatePredecessor,
         };
         store.onDispatchMyMembership(payload);
@@ -142,8 +142,8 @@ describe("RoomListStore", () => {
 
         // When we tell it we joined a new room with no predecessor
         const payload = {
-            oldMembership: "invite",
-            membership: "join",
+            oldMembership: Membership.Invite,
+            membership: Membership.Join,
             room: roomNoPredecessor,
         };
         store.onDispatchMyMembership(payload);
@@ -159,9 +159,9 @@ describe("RoomListStore", () => {
         const room1 = new Room("!r1:e.com", client, userId, { pendingEventOrdering: PendingEventOrdering.Detached });
         const room2 = new Room("!r2:e.com", client, userId, { pendingEventOrdering: PendingEventOrdering.Detached });
         const room3 = new Room("!r3:e.com", client, userId, { pendingEventOrdering: PendingEventOrdering.Detached });
-        room1.updateMyMembership("join");
-        room2.updateMyMembership("join");
-        room3.updateMyMembership("join");
+        room1.updateMyMembership(Membership.Join);
+        room2.updateMyMembership(Membership.Join);
+        room3.updateMyMembership(Membership.Join);
         DMRoomMap.makeShared(client);
         const { store } = createStore();
         client.getVisibleRooms = jest.fn().mockReturnValue([room1, room2, room3]);
@@ -259,8 +259,8 @@ describe("RoomListStore", () => {
             // When we tell it we joined a new room that has an old room as
             // predecessor in the create event
             const payload = {
-                oldMembership: "invite",
-                membership: "join",
+                oldMembership: Membership.Invite,
+                membership: Membership.Join,
                 room: roomWithPredecessorEvent,
             };
             store.onDispatchMyMembership(payload);
diff --git a/test/stores/room-list/algorithms/Algorithm-test.ts b/test/stores/room-list/algorithms/Algorithm-test.ts
index 1fd9d3868e..315c3690e0 100644
--- a/test/stores/room-list/algorithms/Algorithm-test.ts
+++ b/test/stores/room-list/algorithms/Algorithm-test.ts
@@ -82,7 +82,7 @@ describe("Algorithm", () => {
         client.reEmitter.reEmit(room, [RoomStateEvent.Events]);
         client.reEmitter.reEmit(roomWithCall, [RoomStateEvent.Events]);
 
-        for (const room of client.getRooms()) jest.spyOn(room, "getMyMembership").mockReturnValue("join");
+        for (const room of client.getRooms()) jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Join);
         algorithm.setKnownRooms(client.getRooms());
 
         setupAsyncStoreWithClient(CallStore.instance, client);
diff --git a/test/stores/room-list/algorithms/RecentAlgorithm-test.ts b/test/stores/room-list/algorithms/RecentAlgorithm-test.ts
index a58254ca6e..227cdbb0e8 100644
--- a/test/stores/room-list/algorithms/RecentAlgorithm-test.ts
+++ b/test/stores/room-list/algorithms/RecentAlgorithm-test.ts
@@ -53,7 +53,7 @@ describe("RecentAlgorithm", () => {
                 event: true,
             });
 
-            room.getMyMembership = () => "join";
+            room.getMyMembership = () => Membership.Join;
 
             room.addLiveEvents([event1]);
             expect(algorithm.getLastTs(room, "@jane:matrix.org")).toBe(5);
@@ -74,7 +74,7 @@ describe("RecentAlgorithm", () => {
 
         it("works when not a member", () => {
             const room = mkRoom(cli, "!new:example.org");
-            room.getMyMembership.mockReturnValue(EffectiveMembership.Invite);
+            room.getMyMembership.mockReturnValue(Membership.Invite);
             expect(algorithm.getLastTs(room, "@john:matrix.org")).toBe(Number.MAX_SAFE_INTEGER);
         });
     });
@@ -84,8 +84,8 @@ describe("RecentAlgorithm", () => {
             const room1 = new Room("room1", cli, "@bob:matrix.org");
             const room2 = new Room("room2", cli, "@bob:matrix.org");
 
-            room1.getMyMembership = () => "join";
-            room2.getMyMembership = () => "join";
+            room1.getMyMembership = () => Membership.Join;
+            room2.getMyMembership = () => Membership.Join;
 
             const evt = mkMessage({
                 room: room1.roomId,
@@ -112,8 +112,8 @@ describe("RecentAlgorithm", () => {
             const room1 = new Room("room1", cli, "@bob:matrix.org");
             const room2 = new Room("room2", cli, "@bob:matrix.org");
 
-            room1.getMyMembership = () => "join";
-            room2.getMyMembership = () => "join";
+            room1.getMyMembership = () => Membership.Join;
+            room2.getMyMembership = () => Membership.Join;
 
             const evt = mkMessage({
                 room: room1.roomId,
@@ -142,8 +142,8 @@ describe("RecentAlgorithm", () => {
             const room1 = new Room("room1", cli, "@bob:matrix.org");
             const room2 = new Room("room2", cli, "@bob:matrix.org");
 
-            room1.getMyMembership = () => "join";
-            room2.getMyMembership = () => "join";
+            room1.getMyMembership = () => Membership.Join;
+            room2.getMyMembership = () => Membership.Join;
 
             const { rootEvent, events: events1 } = mkThread({
                 room: room1,
diff --git a/test/test-utils/room.ts b/test/test-utils/room.ts
index 5d188fb0b6..4d533e174a 100644
--- a/test/test-utils/room.ts
+++ b/test/test-utils/room.ts
@@ -22,7 +22,7 @@ import { TimelineRenderingType } from "../../src/contexts/RoomContext";
 import { Layout } from "../../src/settings/enums/Layout";
 import { mkEvent } from "./test-utils";
 
-export const makeMembershipEvent = (roomId: string, userId: string, membership = "join") =>
+export const makeMembershipEvent = (roomId: string, userId: string, membership = Membership.Join) =>
     mkEvent({
         event: true,
         type: EventType.RoomMember,
diff --git a/test/test-utils/test-utils.ts b/test/test-utils/test-utils.ts
index 94ec448826..1c9e3e499c 100644
--- a/test/test-utils/test-utils.ts
+++ b/test/test-utils/test-utils.ts
@@ -391,7 +391,7 @@ export function mkEvent(opts: MakeEventProps): MatrixEvent {
     if (!mxEvent.sender && opts.user && opts.room) {
         mxEvent.sender = {
             userId: opts.user,
-            membership: "join",
+            membership: Membership.Join,
             name: opts.user,
             rawDisplayName: opts.user,
             roomId: opts.room,
@@ -465,8 +465,8 @@ export async function mkEncryptedEvent(opts: {
 export function mkMembership(
     opts: MakeEventPassThruProps & {
         room: Room["roomId"];
-        mship: string;
-        prevMship?: string;
+        mship: Membership;
+        prevMship?: Membership;
         name?: string;
         url?: string;
         skey?: string;
@@ -506,7 +506,7 @@ export function mkMembership(
 export function mkRoomMember(
     roomId: string,
     userId: string,
-    membership = "join",
+    membership = Membership.Join,
     isKicked = false,
     prevMemberContent: Partial<IContent> = {},
 ): RoomMember {
@@ -654,7 +654,7 @@ export function mkStubRoom(
         getMembers: jest.fn().mockReturnValue([]),
         getMembersWithMembership: jest.fn().mockReturnValue([]),
         getMxcAvatarUrl: () => "mxc://avatar.url/room.png",
-        getMyMembership: jest.fn().mockReturnValue("join"),
+        getMyMembership: jest.fn().mockReturnValue(Membership.Join),
         getPendingEvents: () => [] as MatrixEvent[],
         getReceiptsForEvent: jest.fn().mockReturnValue([]),
         getRecommendedVersion: jest.fn().mockReturnValue(Promise.resolve("")),
@@ -794,7 +794,7 @@ export const mkRoomMemberJoinEvent = (user: string, room: string, content?: ICon
         event: true,
         type: EventType.RoomMember,
         content: {
-            membership: "join",
+            membership: Membership.Join,
             ...content,
         },
         skey: user,
diff --git a/test/utils/MultiInviter-test.ts b/test/utils/MultiInviter-test.ts
index 55c40b34e9..e61a3faac6 100644
--- a/test/utils/MultiInviter-test.ts
+++ b/test/utils/MultiInviter-test.ts
@@ -168,10 +168,10 @@ describe("MultiInviter", () => {
             const room = new Room(ROOMID, client, client.getSafeUserId());
             mocked(client.getRoom).mockReturnValue(room);
             const ourMember = new RoomMember(ROOMID, client.getSafeUserId());
-            ourMember.membership = "join";
+            ourMember.membership = Membership.Join;
             ourMember.powerLevel = 100;
             const member = new RoomMember(ROOMID, MXID1);
-            member.membership = "ban";
+            member.membership = Membership.Ban;
             member.powerLevel = 0;
             room.getMember = (userId: string) => {
                 if (userId === client.getSafeUserId()) return ourMember;
diff --git a/test/utils/direct-messages-test.ts b/test/utils/direct-messages-test.ts
index 3e697fb459..529fa547a1 100644
--- a/test/utils/direct-messages-test.ts
+++ b/test/utils/direct-messages-test.ts
@@ -86,7 +86,7 @@ describe("direct-messages", () => {
         });
 
         room1 = new Room("!room1:example.com", mockClient, userId1);
-        room1.getMyMembership = () => "join";
+        room1.getMyMembership = () => Membership.Join;
 
         localRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + "test", mockClient, userId1);
 
diff --git a/test/utils/dm/createDmLocalRoom-test.ts b/test/utils/dm/createDmLocalRoom-test.ts
index dc0bcab555..dec7cb98e7 100644
--- a/test/utils/dm/createDmLocalRoom-test.ts
+++ b/test/utils/dm/createDmLocalRoom-test.ts
@@ -37,16 +37,16 @@ function assertLocalRoom(room: LocalRoom, targets: Member[], encrypted: boolean)
     expect(room.name).toBe(targets.length ? targets[0].name : "Empty Room");
     expect(room.encrypted).toBe(encrypted);
     expect(room.targets).toEqual(targets);
-    expect(room.getMyMembership()).toBe("join");
+    expect(room.getMyMembership()).toBe(Membership.Join);
 
     const roomCreateEvent = room.currentState.getStateEvents(EventType.RoomCreate)[0];
     expect(roomCreateEvent).toBeDefined();
     expect(roomCreateEvent.getContent()["room_version"]).toBe(KNOWN_SAFE_ROOM_VERSION);
 
     // check that the user and all targets are joined
-    expect(room.getMember("@userId:matrix.org")?.membership).toBe("join");
+    expect(room.getMember("@userId:matrix.org").membership).toBe(Membership.Join);
     targets.forEach((target: Member) => {
-        expect(room.getMember(target.userId)?.membership).toBe("join");
+        expect(room.getMember(target.userId).membership).toBe(Membership.Join);
     });
 
     if (encrypted) {
diff --git a/test/utils/dm/findDMForUser-test.ts b/test/utils/dm/findDMForUser-test.ts
index 72a7764df6..75b3e001d7 100644
--- a/test/utils/dm/findDMForUser-test.ts
+++ b/test/utils/dm/findDMForUser-test.ts
@@ -51,32 +51,32 @@ describe("findDMForUser", () => {
         mocked(getFunctionalMembers).mockReturnValue([botId]);
 
         room1 = new Room("!room1:example.com", mockClient, userId1);
-        room1.getMyMembership = () => "join";
+        room1.getMyMembership = () => Membership.Join;
         room1.currentState.setStateEvents([
-            makeMembershipEvent(room1.roomId, userId1, "join"),
-            makeMembershipEvent(room1.roomId, userId2, "join"),
+            makeMembershipEvent(room1.roomId, userId1, Membership.Join),
+            makeMembershipEvent(room1.roomId, userId2, Membership.Join),
         ]);
 
         // this should not be a DM room because it is a local room
         room2 = new LocalRoom("!room2:example.com", mockClient, userId1);
-        room2.getMyMembership = () => "join";
+        room2.getMyMembership = () => Membership.Join;
         room2.getLastActiveTimestamp = () => 100;
 
         room3 = new Room("!room3:example.com", mockClient, userId1);
-        room3.getMyMembership = () => "join";
+        room3.getMyMembership = () => Membership.Join;
         room3.currentState.setStateEvents([
-            makeMembershipEvent(room3.roomId, userId1, "join"),
-            makeMembershipEvent(room3.roomId, userId2, "join"),
+            makeMembershipEvent(room3.roomId, userId1, Membership.Join),
+            makeMembershipEvent(room3.roomId, userId2, Membership.Join),
             // Adding the bot user here. Should be excluded when determining if the room is a DM.
-            makeMembershipEvent(room3.roomId, botId, "join"),
+            makeMembershipEvent(room3.roomId, botId, Membership.Join),
         ]);
 
         // this should not be a DM room because it has only one joined user
         room4 = new Room("!room4:example.com", mockClient, userId1);
-        room4.getMyMembership = () => "join";
+        room4.getMyMembership = () => Membership.Join;
         room4.currentState.setStateEvents([
-            makeMembershipEvent(room4.roomId, userId1, "invite"),
-            makeMembershipEvent(room4.roomId, userId2, "join"),
+            makeMembershipEvent(room4.roomId, userId1, Membership.Invite),
+            makeMembershipEvent(room4.roomId, userId2, Membership.Join),
         ]);
 
         // this should not be a DM room because it has no users
@@ -85,17 +85,17 @@ describe("findDMForUser", () => {
 
         // room not correctly stored in userId → room map; should be found by the "all rooms" fallback
         room6 = new Room("!room6:example.com", mockClient, userId1);
-        room6.getMyMembership = () => "join";
+        room6.getMyMembership = () => Membership.Join;
         room6.currentState.setStateEvents([
-            makeMembershipEvent(room6.roomId, userId1, "join"),
-            makeMembershipEvent(room6.roomId, userId3, "join"),
+            makeMembershipEvent(room6.roomId, userId1, Membership.Join),
+            makeMembershipEvent(room6.roomId, userId3, Membership.Join),
         ]);
 
         // room with pending third-party invite
         room7 = new Room("!room7:example.com", mockClient, userId1);
-        room7.getMyMembership = () => "join";
+        room7.getMyMembership = () => Membership.Join;
         room7.currentState.setStateEvents([
-            makeMembershipEvent(room7.roomId, userId1, "join"),
+            makeMembershipEvent(room7.roomId, userId1, Membership.Join),
             mkThirdPartyInviteEvent(thirdPartyId, "third-party", room7.roomId),
         ]);
 
diff --git a/test/utils/export-test.tsx b/test/utils/export-test.tsx
index 8565c42ef6..64204af423 100644
--- a/test/utils/export-test.tsx
+++ b/test/utils/export-test.tsx
@@ -201,8 +201,8 @@ describe("export", function () {
                         getMxcAvatarUrl: () => "mxc://avatar.url/image.png",
                     } as unknown as RoomMember,
                     ts: ts0 + i * 1000,
-                    mship: "join",
-                    prevMship: "join",
+                    mship: Membership.Join,
+                    prevMship: Membership.Join,
                     name: "A user",
                 }),
             );
diff --git a/test/utils/local-room-test.ts b/test/utils/local-room-test.ts
index 7aadef65a9..2ebc51e179 100644
--- a/test/utils/local-room-test.ts
+++ b/test/utils/local-room-test.ts
@@ -36,7 +36,7 @@ describe("local-room", () => {
     beforeEach(() => {
         client = createTestClient();
         room1 = new Room("!room1:example.com", client, userId1);
-        room1.getMyMembership = () => "join";
+        room1.getMyMembership = () => Membership.Join;
         localRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + "test", client, "@test:example.com");
         mocked(client.getRoom).mockImplementation((roomId: string) => {
             if (roomId === localRoom.roomId) {
diff --git a/test/utils/localRoom/isRoomReady-test.ts b/test/utils/localRoom/isRoomReady-test.ts
index e317301e76..786104e446 100644
--- a/test/utils/localRoom/isRoomReady-test.ts
+++ b/test/utils/localRoom/isRoomReady-test.ts
@@ -33,7 +33,7 @@ describe("isRoomReady", () => {
     beforeEach(() => {
         client = createTestClient();
         room1 = new Room("!room1:example.com", client, userId1);
-        room1.getMyMembership = () => "join";
+        room1.getMyMembership = () => Membership.Join;
         localRoom = new LocalRoom(LOCAL_ROOM_ID_PREFIX + "test", client, "@test:example.com");
     });
 
@@ -70,8 +70,8 @@ describe("isRoomReady", () => {
             describe("and all members have been invited or joined", () => {
                 beforeEach(() => {
                     room1.currentState.setStateEvents([
-                        makeMembershipEvent(room1.roomId, userId1, "join"),
-                        makeMembershipEvent(room1.roomId, userId2, "invite"),
+                        makeMembershipEvent(room1.roomId, userId1, Membership.Join),
+                        makeMembershipEvent(room1.roomId, userId2, Membership.Invite),
                     ]);
                 });
 
diff --git a/test/utils/membership-test.ts b/test/utils/membership-test.ts
index 3561625573..24dedd8206 100644
--- a/test/utils/membership-test.ts
+++ b/test/utils/membership-test.ts
@@ -31,16 +31,16 @@ describe("isKnockDenied", () => {
     });
 
     it("checks that the user knock has been denied", () => {
-        const roomMember = mkRoomMember(room.roomId, userId, "leave", true, { membership: "knock" });
+        const roomMember = mkRoomMember(room.roomId, userId, Membership.Leave, true, { membership: Membership.Knock });
         jest.spyOn(room, "getMember").mockReturnValue(roomMember);
         expect(isKnockDenied(room)).toBe(true);
     });
 
     it.each([
-        { membership: "leave", isKicked: false, prevMembership: "invite" },
-        { membership: "leave", isKicked: true, prevMembership: "invite" },
-        { membership: "leave", isKicked: false, prevMembership: "join" },
-        { membership: "leave", isKicked: true, prevMembership: "join" },
+        { membership: Membership.Leave, isKicked: false, prevMembership: Membership.Invite },
+        { membership: Membership.Leave, isKicked: true, prevMembership: Membership.Invite },
+        { membership: Membership.Leave, isKicked: false, prevMembership: Membership.Join },
+        { membership: Membership.Leave, isKicked: true, prevMembership: Membership.Join },
     ])("checks that the user knock has been not denied", ({ membership, isKicked, prevMembership }) => {
         const roomMember = mkRoomMember(room.roomId, userId, membership, isKicked, { membership: prevMembership });
         jest.spyOn(room, "getMember").mockReturnValue(roomMember);
diff --git a/test/utils/permalinks/Permalinks-test.ts b/test/utils/permalinks/Permalinks-test.ts
index 8e85aac02c..be2e206055 100644
--- a/test/utils/permalinks/Permalinks-test.ts
+++ b/test/utils/permalinks/Permalinks-test.ts
@@ -45,7 +45,7 @@ describe("Permalinks", function () {
         members: RoomMember[],
         serverACLContent?: { deny?: string[]; allow?: string[] },
     ): Room {
-        members.forEach((m) => (m.membership = "join"));
+        members.forEach((m) => (m.membership = Membership.Join));
         const powerLevelsUsers = members.reduce<Record<string, number>>((pl, member) => {
             if (Number.isFinite(member.powerLevel)) {
                 pl[member.userId] = member.powerLevel;
@@ -152,11 +152,11 @@ describe("Permalinks", function () {
         const creator = new RoomPermalinkCreator(room, null);
         creator.load();
         expect(creator.serverCandidates![0]).toBe("pl_95");
-        member95.membership = "left";
+        member95.membership = Membership.Leave;
         // @ts-ignore illegal private property
         creator.onRoomStateUpdate();
         expect(creator.serverCandidates![0]).toBe("pl_75");
-        member95.membership = "join";
+        member95.membership = Membership.Join;
         // @ts-ignore illegal private property
         creator.onRoomStateUpdate();
         expect(creator.serverCandidates![0]).toBe("pl_95");
diff --git a/test/utils/room/canInviteTo-test.ts b/test/utils/room/canInviteTo-test.ts
index c35202912e..27c3660ca0 100644
--- a/test/utils/room/canInviteTo-test.ts
+++ b/test/utils/room/canInviteTo-test.ts
@@ -39,7 +39,7 @@ describe("canInviteTo()", () => {
             ...mockClientMethodsUser(userId),
         });
         const room = new Room(roomId, client, userId);
-        jest.spyOn(room, "getMyMembership").mockReturnValue("join");
+        jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Join);
         jest.spyOn(room, "getJoinRule").mockReturnValue(JoinRule.Public);
         jest.spyOn(room, "canInvite").mockReturnValue(true);
         return room;
@@ -54,7 +54,7 @@ describe("canInviteTo()", () => {
 
         it("should return false when current user membership is not joined", () => {
             const room = makeRoom();
-            jest.spyOn(room, "getMyMembership").mockReturnValue("invite");
+            jest.spyOn(room, "getMyMembership").mockReturnValue(Membership.Invite);
 
             expect(canInviteTo(room)).toEqual(false);
         });