From 1281c0746b1aacb54c1eae3de6cb31b40f97d64e Mon Sep 17 00:00:00 2001
From: Michael Telatynski <7t3chguy@gmail.com>
Date: Fri, 28 Apr 2023 09:45:36 +0100
Subject: [PATCH] Conform more of the codebase to `strictNullChecks` (#10731)

---
 src/@types/common.ts                          |  2 +
 src/LegacyCallHandler.tsx                     | 10 +++--
 src/Modal.tsx                                 |  8 ++--
 .../security/RecoveryMethodRemovedDialog.tsx  |  6 +--
 src/components/structures/InteractiveAuth.tsx |  4 +-
 .../structures/LegacyCallEventGrouper.ts      | 17 +++++---
 src/components/structures/LoggedInView.tsx    |  4 +-
 src/components/structures/MatrixChat.tsx      |  6 ++-
 src/components/structures/MessagePanel.tsx    |  8 +++-
 src/components/structures/PipContainer.tsx    |  6 +--
 src/components/structures/RightPanel.tsx      |  2 +-
 src/components/structures/SearchBox.tsx       |  8 ++--
 .../structures/auth/Registration.tsx          |  4 +-
 .../dialogs/AnalyticsLearnMoreDialog.tsx      | 12 +++---
 .../views/dialogs/BetaFeedbackDialog.tsx      |  2 +-
 .../views/dialogs/FeedbackDialog.tsx          | 17 ++++----
 .../dialogs/GenericFeatureFeedbackDialog.tsx  |  7 ++--
 .../ManualDeviceKeyVerificationDialog.tsx     |  4 +-
 .../views/dialogs/ModuleUiDialog.tsx          |  2 +-
 .../views/dialogs/ServerPickerDialog.tsx      |  6 ++-
 src/components/views/dialogs/ShareDialog.tsx  | 42 ++++++++++---------
 .../dialogs/SlidingSyncOptionsDialog.tsx      |  2 +-
 .../security/AccessSecretStorageDialog.tsx    |  2 +-
 .../security/RestoreKeyBackupDialog.tsx       |  4 +-
 src/components/views/rooms/EventTile.tsx      |  8 ++--
 .../views/rooms/ThirdPartyMemberInfo.tsx      |  6 +--
 .../views/settings/SecureBackupPanel.tsx      |  4 +-
 .../views/settings/devices/deleteDevices.tsx  |  8 ++--
 .../settings/tabs/user/SessionManagerTab.tsx  |  2 +-
 src/dispatcher/payloads/ViewRoomPayload.ts    | 18 ++++----
 src/linkify-matrix.ts                         |  2 +-
 src/rageshake/submit-rageshake.ts             |  5 +--
 src/settings/Settings.tsx                     |  2 +-
 src/stores/right-panel/RightPanelStore.ts     |  2 +-
 .../right-panel/RightPanelStoreIPanelState.ts |  4 +-
 src/utils/UserInteractiveAuth.ts              |  8 ++--
 src/utils/localRoom/isLocalRoom.ts            |  2 +-
 .../structures/LoggedInView-test.tsx          |  1 +
 .../structures/RoomSearchView-test.tsx        |  8 ++--
 .../AccessSecretStorageDialog-test.tsx        |  1 +
 40 files changed, 147 insertions(+), 119 deletions(-)

diff --git a/src/@types/common.ts b/src/@types/common.ts
index e6e69ab1ec..4aeea8a643 100644
--- a/src/@types/common.ts
+++ b/src/@types/common.ts
@@ -76,3 +76,5 @@ interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
 type DeepReadonlyObject<T> = {
     readonly [P in keyof T]: DeepReadonly<T[P]>;
 };
+
+export type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
diff --git a/src/LegacyCallHandler.tsx b/src/LegacyCallHandler.tsx
index 71891a3e13..bf0d09db29 100644
--- a/src/LegacyCallHandler.tsx
+++ b/src/LegacyCallHandler.tsx
@@ -272,7 +272,8 @@ export default class LegacyCallHandler extends EventEmitter {
         return localNotificationsAreSilenced(cli);
     }
 
-    public silenceCall(callId: string): void {
+    public silenceCall(callId?: string): void {
+        if (!callId) return;
         this.silencedCalls.add(callId);
         this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls);
 
@@ -281,8 +282,8 @@ export default class LegacyCallHandler extends EventEmitter {
         this.pause(AudioID.Ring);
     }
 
-    public unSilenceCall(callId: string): void {
-        if (this.isForcedSilent()) return;
+    public unSilenceCall(callId?: string): void {
+        if (!callId || this.isForcedSilent()) return;
         this.silencedCalls.delete(callId);
         this.emit(LegacyCallHandlerEvent.SilencedCallsChanged, this.silencedCalls);
         this.play(AudioID.Ring);
@@ -1182,8 +1183,9 @@ export default class LegacyCallHandler extends EventEmitter {
         // Prevent double clicking the call button
         const widget = WidgetStore.instance.getApps(roomId).find((app) => WidgetType.JITSI.matches(app.type));
         if (widget) {
+            const room = client.getRoom(roomId);
             // If there already is a Jitsi widget, pin it
-            WidgetLayoutStore.instance.moveToContainer(client.getRoom(roomId), widget, Container.Top);
+            if (room) WidgetLayoutStore.instance.moveToContainer(room, widget, Container.Top);
             return;
         }
 
diff --git a/src/Modal.tsx b/src/Modal.tsx
index e8c514b801..d78758685b 100644
--- a/src/Modal.tsx
+++ b/src/Modal.tsx
@@ -30,7 +30,7 @@ const STATIC_DIALOG_CONTAINER_ID = "mx_Dialog_StaticContainer";
 
 // Type which accepts a React Component which looks like a Modal (accepts an onFinished prop)
 export type ComponentType = React.ComponentType<{
-    onFinished?(...args: any): void;
+    onFinished(...args: any): void;
 }>;
 
 // Generic type which returns the props of the Modal component with the onFinished being optional.
@@ -135,7 +135,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
     }
 
     public appendDialog<C extends ComponentType>(
-        Element: React.ComponentType,
+        Element: C,
         props?: ComponentProps<C>,
         className?: string,
     ): IHandle<C> {
@@ -157,7 +157,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
     }
 
     private buildModal<C extends ComponentType>(
-        prom: Promise<React.ComponentType>,
+        prom: Promise<C>,
         props?: ComponentProps<C>,
         className?: string,
         options?: IOptions<C>,
@@ -301,7 +301,7 @@ export class ModalManager extends TypedEventEmitter<ModalManagerEvent, HandlerMa
     }
 
     private appendDialogAsync<C extends ComponentType>(
-        prom: Promise<React.ComponentType>,
+        prom: Promise<C>,
         props?: ComponentProps<C>,
         className?: string,
     ): IHandle<C> {
diff --git a/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx b/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx
index 7363f192a2..b5983b66c0 100644
--- a/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx
+++ b/src/async-components/views/dialogs/security/RecoveryMethodRemovedDialog.tsx
@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import React, { ComponentType } from "react";
+import React from "react";
 
 import dis from "../../../../dispatcher/dispatcher";
 import { _t } from "../../../../languageHandler";
-import Modal from "../../../../Modal";
+import Modal, { ComponentType } from "../../../../Modal";
 import { Action } from "../../../../dispatcher/actions";
 import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
 import DialogButtons from "../../../../components/views/elements/DialogButtons";
@@ -37,7 +37,7 @@ export default class RecoveryMethodRemovedDialog extends React.PureComponent<IPr
     private onSetupClick = (): void => {
         this.props.onFinished();
         Modal.createDialogAsync(
-            import("./CreateKeyBackupDialog") as unknown as Promise<ComponentType<{}>>,
+            import("./CreateKeyBackupDialog") as unknown as Promise<ComponentType>,
             undefined,
             undefined,
             /* priority = */ false,
diff --git a/src/components/structures/InteractiveAuth.tsx b/src/components/structures/InteractiveAuth.tsx
index b3c8fc26fd..e1427c8a7b 100644
--- a/src/components/structures/InteractiveAuth.tsx
+++ b/src/components/structures/InteractiveAuth.tsx
@@ -62,7 +62,7 @@ export interface InteractiveAuthProps<T> {
     continueText?: string;
     continueKind?: string;
     // callback
-    makeRequest(auth?: IAuthData): Promise<UIAResponse<T>>;
+    makeRequest(auth: IAuthDict | null): Promise<UIAResponse<T>>;
     // callback called when the auth process has finished,
     // successfully or unsuccessfully.
     // @param {boolean} status True if the operation requiring
@@ -200,7 +200,7 @@ export default class InteractiveAuthComponent<T> extends React.Component<Interac
         );
     };
 
-    private requestCallback = (auth: IAuthData | null, background: boolean): Promise<IAuthData> => {
+    private requestCallback = (auth: IAuthDict | null, background: boolean): Promise<UIAResponse<T>> => {
         // This wrapper just exists because the js-sdk passes a second
         // 'busy' param for backwards compat. This throws the tests off
         // so discard it here.
diff --git a/src/components/structures/LegacyCallEventGrouper.ts b/src/components/structures/LegacyCallEventGrouper.ts
index d28224729a..0fcb91992f 100644
--- a/src/components/structures/LegacyCallEventGrouper.ts
+++ b/src/components/structures/LegacyCallEventGrouper.ts
@@ -152,15 +152,21 @@ export default class LegacyCallEventGrouper extends EventEmitter {
     };
 
     public answerCall = (): void => {
-        LegacyCallHandler.instance.answerCall(this.roomId);
+        const roomId = this.roomId;
+        if (!roomId) return;
+        LegacyCallHandler.instance.answerCall(roomId);
     };
 
     public rejectCall = (): void => {
-        LegacyCallHandler.instance.hangupOrReject(this.roomId, true);
+        const roomId = this.roomId;
+        if (!roomId) return;
+        LegacyCallHandler.instance.hangupOrReject(roomId, true);
     };
 
     public callBack = (): void => {
-        LegacyCallHandler.instance.placeCall(this.roomId, this.isVoice ? CallType.Voice : CallType.Video);
+        const roomId = this.roomId;
+        if (!roomId) return;
+        LegacyCallHandler.instance.placeCall(roomId, this.isVoice ? CallType.Voice : CallType.Video);
     };
 
     public toggleSilenced = (): void => {
@@ -191,9 +197,10 @@ export default class LegacyCallEventGrouper extends EventEmitter {
     };
 
     private setCall = (): void => {
-        if (this.call) return;
+        const callId = this.callId;
+        if (!callId || this.call) return;
 
-        this.call = LegacyCallHandler.instance.getCallById(this.callId);
+        this.call = LegacyCallHandler.instance.getCallById(callId);
         this.setCallListeners();
         this.setState();
     };
diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx
index afc35508ac..fb3cb8e291 100644
--- a/src/components/structures/LoggedInView.tsx
+++ b/src/components/structures/LoggedInView.tsx
@@ -99,7 +99,7 @@ interface IProps {
     currentRoomId: string;
     collapseLhs: boolean;
     config: ConfigOptions;
-    currentUserId?: string;
+    currentUserId: string;
     justRegistered?: boolean;
     roomJustCreatedOpts?: IOpts;
     forceTimeline?: boolean; // see props on MatrixChat
@@ -360,7 +360,7 @@ class LoggedInView extends React.Component<IProps, IState> {
             }
         }
 
-        if (pinnedEventTs && this.state.usageLimitEventTs > pinnedEventTs) {
+        if (pinnedEventTs && this.state.usageLimitEventTs && this.state.usageLimitEventTs > pinnedEventTs) {
             // We've processed a newer event than this one, so ignore it.
             return;
         }
diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx
index 07590446a3..d614a5edf3 100644
--- a/src/components/structures/MatrixChat.tsx
+++ b/src/components/structures/MatrixChat.tsx
@@ -422,7 +422,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
     public componentDidUpdate(prevProps: IProps, prevState: IState): void {
         if (this.shouldTrackPageChange(prevState, this.state)) {
             const durationMs = this.stopPageChangeTimer();
-            PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
+            if (durationMs != null) {
+                PosthogTrackers.instance.trackPageChange(this.state.view, this.state.page_type, durationMs);
+            }
         }
         if (this.focusComposer) {
             dis.fire(Action.FocusSendMessageComposer);
@@ -935,7 +937,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
             await this.firstSyncPromise.promise;
         }
 
-        let presentedId = roomInfo.room_alias || roomInfo.room_id;
+        let presentedId = roomInfo.room_alias || roomInfo.room_id!;
         const room = MatrixClientPeg.get().getRoom(roomInfo.room_id);
         if (room) {
             // Not all timeline events are decrypted ahead of time anymore
diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx
index 3d48c925fe..56d0db128b 100644
--- a/src/components/structures/MessagePanel.tsx
+++ b/src/components/structures/MessagePanel.tsx
@@ -308,7 +308,11 @@ export default class MessagePanel extends React.Component<IProps, IState> {
             this.calculateRoomMembersCount();
         }
 
-        if (prevProps.readMarkerVisible && this.props.readMarkerEventId !== prevProps.readMarkerEventId) {
+        if (
+            prevProps.readMarkerVisible &&
+            prevProps.readMarkerEventId &&
+            this.props.readMarkerEventId !== prevProps.readMarkerEventId
+        ) {
             const ghostReadMarkers = this.state.ghostReadMarkers;
             ghostReadMarkers.push(prevProps.readMarkerEventId);
             this.setState({
@@ -906,7 +910,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
             if (receiptsByUserId.get(userId)) {
                 continue;
             }
-            const { lastShownEventId, receipt } = this.readReceiptsByUserId.get(userId);
+            const { lastShownEventId, receipt } = this.readReceiptsByUserId.get(userId)!;
             const existingReceipts = receiptsByEvent.get(lastShownEventId) || [];
             receiptsByEvent.set(lastShownEventId, existingReceipts.concat(receipt));
             receiptsByUserId.set(userId, { lastShownEventId, receipt });
diff --git a/src/components/structures/PipContainer.tsx b/src/components/structures/PipContainer.tsx
index 8ef9b1b816..6d761f9c79 100644
--- a/src/components/structures/PipContainer.tsx
+++ b/src/components/structures/PipContainer.tsx
@@ -239,7 +239,7 @@ class PipContainerInner extends React.Component<IProps, IState> {
         let notDocked = false;
         // Sanity check the room - the widget may have been destroyed between render cycles, and
         // thus no room is associated anymore.
-        if (persistentWidgetId && MatrixClientPeg.get().getRoom(persistentRoomId)) {
+        if (persistentWidgetId && persistentRoomId && MatrixClientPeg.get().getRoom(persistentRoomId)) {
             notDocked = !ActiveWidgetStore.instance.isDocked(persistentWidgetId, persistentRoomId);
             fromAnotherRoom = this.state.viewedRoomId !== persistentRoomId;
         }
@@ -314,10 +314,10 @@ class PipContainerInner extends React.Component<IProps, IState> {
             ));
         }
 
-        if (this.state.showWidgetInPip) {
+        if (this.state.showWidgetInPip && this.state.persistentWidgetId) {
             pipContent.push(({ onStartMoving }) => (
                 <WidgetPip
-                    widgetId={this.state.persistentWidgetId}
+                    widgetId={this.state.persistentWidgetId!}
                     room={MatrixClientPeg.get().getRoom(this.state.persistentRoomId ?? undefined)!}
                     viewingRoom={this.state.viewedRoomId === this.state.persistentRoomId}
                     onStartMoving={onStartMoving}
diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx
index d1808ea79a..d4ebb198bf 100644
--- a/src/components/structures/RightPanel.tsx
+++ b/src/components/structures/RightPanel.tsx
@@ -207,7 +207,7 @@ export default class RightPanel extends React.Component<IProps, IState> {
                 break;
 
             case RightPanelPhases.PinnedMessages:
-                if (SettingsStore.getValue("feature_pinning")) {
+                if (this.props.room && SettingsStore.getValue("feature_pinning")) {
                     card = (
                         <PinnedMessagesCard
                             room={this.props.room}
diff --git a/src/components/structures/SearchBox.tsx b/src/components/structures/SearchBox.tsx
index 16c2df3173..39d870044e 100644
--- a/src/components/structures/SearchBox.tsx
+++ b/src/components/structures/SearchBox.tsx
@@ -62,7 +62,7 @@ export default class SearchBox extends React.Component<IProps, IState> {
 
     private onSearch = throttle(
         (): void => {
-            this.props.onSearch(this.search.current?.value);
+            this.props.onSearch(this.search.current?.value ?? "");
         },
         200,
         { trailing: true, leading: true },
@@ -94,11 +94,9 @@ export default class SearchBox extends React.Component<IProps, IState> {
     };
 
     private clearSearch(source?: string): void {
-        this.search.current.value = "";
+        if (this.search.current) this.search.current.value = "";
         this.onChange();
-        if (this.props.onCleared) {
-            this.props.onCleared(source);
-        }
+        this.props.onCleared?.(source);
     }
 
     public render(): React.ReactNode {
diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx
index 86d909748e..045ee6fccf 100644
--- a/src/components/structures/auth/Registration.tsx
+++ b/src/components/structures/auth/Registration.tsx
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import { AuthType, createClient, IAuthData, IInputs, MatrixError } from "matrix-js-sdk/src/matrix";
+import { AuthType, createClient, IAuthDict, IAuthData, IInputs, MatrixError } from "matrix-js-sdk/src/matrix";
 import React, { Fragment, ReactNode } from "react";
 import { IRegisterRequestParams, IRequestTokenResponse, MatrixClient } from "matrix-js-sdk/src/client";
 import classNames from "classnames";
@@ -461,7 +461,7 @@ export default class Registration extends React.Component<IProps, IState> {
         });
     };
 
-    private makeRegisterRequest = (auth: IAuthData | null): Promise<IAuthData> => {
+    private makeRegisterRequest = (auth: IAuthDict | null): Promise<IAuthData> => {
         if (!this.state.matrixClient) throw new Error("Matrix client has not yet been loaded");
 
         const registerParams: IRegisterRequestParams = {
diff --git a/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx b/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx
index e87fc16ef9..f4cf78681b 100644
--- a/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx
+++ b/src/components/views/dialogs/AnalyticsLearnMoreDialog.tsx
@@ -19,7 +19,7 @@ import React from "react";
 import BaseDialog from "./BaseDialog";
 import { _t } from "../../../languageHandler";
 import DialogButtons from "../elements/DialogButtons";
-import Modal from "../../../Modal";
+import Modal, { ComponentProps } from "../../../Modal";
 import SdkConfig from "../../../SdkConfig";
 import { getPolicyUrl } from "../../../toasts/AnalyticsToast";
 
@@ -29,7 +29,7 @@ export enum ButtonClicked {
 }
 
 interface IProps {
-    onFinished?(buttonClicked?: ButtonClicked): void;
+    onFinished(buttonClicked?: ButtonClicked): void;
     analyticsOwner: string;
     privacyPolicyUrl?: string;
     primaryButton?: string;
@@ -45,8 +45,8 @@ export const AnalyticsLearnMoreDialog: React.FC<IProps> = ({
     cancelButton,
     hasCancel,
 }) => {
-    const onPrimaryButtonClick = (): void => onFinished?.(ButtonClicked.Primary);
-    const onCancelButtonClick = (): void => onFinished?.(ButtonClicked.Cancel);
+    const onPrimaryButtonClick = (): void => onFinished(ButtonClicked.Primary);
+    const onCancelButtonClick = (): void => onFinished(ButtonClicked.Cancel);
     const privacyPolicyLink = privacyPolicyUrl ? (
         <span>
             {_t(
@@ -114,7 +114,9 @@ export const AnalyticsLearnMoreDialog: React.FC<IProps> = ({
     );
 };
 
-export const showDialog = (props: Omit<IProps, "cookiePolicyUrl" | "analyticsOwner">): void => {
+export const showDialog = (
+    props: Omit<ComponentProps<typeof AnalyticsLearnMoreDialog>, "cookiePolicyUrl" | "analyticsOwner">,
+): void => {
     const privacyPolicyUrl = getPolicyUrl();
     const analyticsOwner = SdkConfig.get("analytics_owner") ?? SdkConfig.get("brand");
     Modal.createDialog(
diff --git a/src/components/views/dialogs/BetaFeedbackDialog.tsx b/src/components/views/dialogs/BetaFeedbackDialog.tsx
index a262fd4f5e..00bb7cd7f6 100644
--- a/src/components/views/dialogs/BetaFeedbackDialog.tsx
+++ b/src/components/views/dialogs/BetaFeedbackDialog.tsx
@@ -38,7 +38,7 @@ const BetaFeedbackDialog: React.FC<IProps> = ({ featureId, onFinished }) => {
     return (
         <GenericFeatureFeedbackDialog
             title={_t("%(featureName)s Beta feedback", { featureName: info.title })}
-            subheading={_t(info.feedbackSubheading)}
+            subheading={info.feedbackSubheading ? _t(info.feedbackSubheading) : undefined}
             onFinished={onFinished}
             rageshakeLabel={info.feedbackLabel}
             rageshakeData={Object.fromEntries(
diff --git a/src/components/views/dialogs/FeedbackDialog.tsx b/src/components/views/dialogs/FeedbackDialog.tsx
index 6b204d68e1..5c8d1e4acf 100644
--- a/src/components/views/dialogs/FeedbackDialog.tsx
+++ b/src/components/views/dialogs/FeedbackDialog.tsx
@@ -48,14 +48,11 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
         Modal.createDialog(BugReportDialog, {});
     };
 
-    const rageshakeUrl = SdkConfig.get().bug_report_endpoint_url;
-    const hasFeedback = !!rageshakeUrl;
+    const hasFeedback = !!SdkConfig.get().bug_report_endpoint_url;
     const onFinished = (sendFeedback: boolean): void => {
         if (hasFeedback && sendFeedback) {
-            if (rageshakeUrl) {
-                const label = props.feature ? `${props.feature}-feedback` : "feedback";
-                submitFeedback(rageshakeUrl, label, comment, canContact);
-            }
+            const label = props.feature ? `${props.feature}-feedback` : "feedback";
+            submitFeedback(label, comment, canContact);
 
             Modal.createDialog(InfoDialog, {
                 title: _t("Feedback sent"),
@@ -65,8 +62,8 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
         props.onFinished();
     };
 
-    let feedbackSection;
-    if (rageshakeUrl) {
+    let feedbackSection: JSX.Element | undefined;
+    if (hasFeedback) {
         feedbackSection = (
             <div className="mx_FeedbackDialog_section mx_FeedbackDialog_rateApp">
                 <h3>{_t("Comment")}</h3>
@@ -93,8 +90,8 @@ const FeedbackDialog: React.FC<IProps> = (props: IProps) => {
         );
     }
 
-    let bugReports: JSX.Element | null = null;
-    if (rageshakeUrl) {
+    let bugReports: JSX.Element | undefined;
+    if (hasFeedback) {
         bugReports = (
             <p className="mx_FeedbackDialog_section_microcopy">
                 {_t(
diff --git a/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx b/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx
index 30e248a6d6..60acae926c 100644
--- a/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx
+++ b/src/components/views/dialogs/GenericFeatureFeedbackDialog.tsx
@@ -19,7 +19,6 @@ import React, { ReactNode, useState } from "react";
 import QuestionDialog from "./QuestionDialog";
 import { _t } from "../../../languageHandler";
 import Field from "../elements/Field";
-import SdkConfig from "../../../SdkConfig";
 import { submitFeedback } from "../../../rageshake/submit-rageshake";
 import StyledCheckbox from "../elements/StyledCheckbox";
 import Modal from "../../../Modal";
@@ -27,8 +26,8 @@ import InfoDialog from "./InfoDialog";
 
 interface IProps {
     title: string;
-    subheading: string;
-    rageshakeLabel: string;
+    subheading?: string;
+    rageshakeLabel?: string;
     rageshakeData?: Record<string, any>;
     children?: ReactNode;
     onFinished(sendFeedback?: boolean): void;
@@ -48,7 +47,7 @@ const GenericFeatureFeedbackDialog: React.FC<IProps> = ({
     const sendFeedback = async (ok: boolean): Promise<void> => {
         if (!ok) return onFinished(false);
 
-        submitFeedback(SdkConfig.get().bug_report_endpoint_url, rageshakeLabel, comment, canContact, rageshakeData);
+        submitFeedback(rageshakeLabel, comment, canContact, rageshakeData);
         onFinished(true);
 
         Modal.createDialog(InfoDialog, {
diff --git a/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx b/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx
index 8f1da8a253..2e71ff199e 100644
--- a/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx
+++ b/src/components/views/dialogs/ManualDeviceKeyVerificationDialog.tsx
@@ -29,7 +29,7 @@ import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
 interface IManualDeviceKeyVerificationDialogProps {
     userId: string;
     device: Device;
-    onFinished?(confirm?: boolean): void;
+    onFinished(confirm?: boolean): void;
 }
 
 export function ManualDeviceKeyVerificationDialog({
@@ -44,7 +44,7 @@ export function ManualDeviceKeyVerificationDialog({
             if (confirm && mxClient) {
                 mxClient.setDeviceVerified(userId, device.deviceId, true);
             }
-            onFinished?.(confirm);
+            onFinished(confirm);
         },
         [mxClient, userId, device, onFinished],
     );
diff --git a/src/components/views/dialogs/ModuleUiDialog.tsx b/src/components/views/dialogs/ModuleUiDialog.tsx
index cced3907bb..d4f36f8674 100644
--- a/src/components/views/dialogs/ModuleUiDialog.tsx
+++ b/src/components/views/dialogs/ModuleUiDialog.tsx
@@ -47,7 +47,7 @@ export class ModuleUiDialog extends ScrollableBaseModal<IProps, IState> {
 
     protected async submit(): Promise<void> {
         try {
-            const model = await this.contentRef.current.trySubmit();
+            const model = await this.contentRef.current!.trySubmit();
             this.props.onFinished(true, model);
         } catch (e) {
             logger.error("Error during submission of module dialog:", e);
diff --git a/src/components/views/dialogs/ServerPickerDialog.tsx b/src/components/views/dialogs/ServerPickerDialog.tsx
index d7469b8694..aca963e422 100644
--- a/src/components/views/dialogs/ServerPickerDialog.tsx
+++ b/src/components/views/dialogs/ServerPickerDialog.tsx
@@ -120,7 +120,11 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
 
                 // try to carry on anyway
                 try {
-                    this.validatedConf = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(hsUrl, null, true);
+                    this.validatedConf = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(
+                        hsUrl,
+                        undefined,
+                        true,
+                    );
                     return {};
                 } catch (e) {
                     logger.error(e);
diff --git a/src/components/views/dialogs/ShareDialog.tsx b/src/components/views/dialogs/ShareDialog.tsx
index 9fcfee0dc2..295074574d 100644
--- a/src/components/views/dialogs/ShareDialog.tsx
+++ b/src/components/views/dialogs/ShareDialog.tsx
@@ -30,6 +30,7 @@ import SettingsStore from "../../../settings/SettingsStore";
 import { UIFeature } from "../../../settings/UIFeature";
 import BaseDialog from "./BaseDialog";
 import CopyableText from "../elements/CopyableText";
+import { XOR } from "../../../@types/common";
 
 const socials = [
     {
@@ -63,19 +64,27 @@ const socials = [
     },
 ];
 
-interface IProps {
-    target: Room | User | RoomMember | MatrixEvent;
-    permalinkCreator?: RoomPermalinkCreator;
+interface BaseProps {
     onFinished(): void;
 }
 
+interface Props extends BaseProps {
+    target: Room | User | RoomMember;
+    permalinkCreator?: RoomPermalinkCreator;
+}
+
+interface EventProps extends BaseProps {
+    target: MatrixEvent;
+    permalinkCreator: RoomPermalinkCreator;
+}
+
 interface IState {
     linkSpecificEvent: boolean;
     permalinkCreator: RoomPermalinkCreator | null;
 }
 
-export default class ShareDialog extends React.PureComponent<IProps, IState> {
-    public constructor(props: IProps) {
+export default class ShareDialog extends React.PureComponent<XOR<Props, EventProps>, IState> {
+    public constructor(props: XOR<Props, EventProps>) {
         super(props);
 
         let permalinkCreator: RoomPermalinkCreator | null = null;
@@ -103,30 +112,25 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
     };
 
     private getUrl(): string {
-        let matrixToUrl;
-
         if (this.props.target instanceof Room) {
             if (this.state.linkSpecificEvent) {
                 const events = this.props.target.getLiveTimeline().getEvents();
-                matrixToUrl = this.state.permalinkCreator!.forEvent(events[events.length - 1].getId()!);
+                return this.state.permalinkCreator!.forEvent(events[events.length - 1].getId()!);
             } else {
-                matrixToUrl = this.state.permalinkCreator!.forShareableRoom();
+                return this.state.permalinkCreator!.forShareableRoom();
             }
         } else if (this.props.target instanceof User || this.props.target instanceof RoomMember) {
-            matrixToUrl = makeUserPermalink(this.props.target.userId);
-        } else if (this.props.target instanceof MatrixEvent) {
-            if (this.state.linkSpecificEvent) {
-                matrixToUrl = this.props.permalinkCreator.forEvent(this.props.target.getId()!);
-            } else {
-                matrixToUrl = this.props.permalinkCreator.forShareableRoom();
-            }
+            return makeUserPermalink(this.props.target.userId);
+        } else if (this.state.linkSpecificEvent) {
+            return this.props.permalinkCreator!.forEvent(this.props.target.getId()!);
+        } else {
+            return this.props.permalinkCreator!.forShareableRoom();
         }
-        return matrixToUrl;
     }
 
     public render(): React.ReactNode {
-        let title;
-        let checkbox;
+        let title: string | undefined;
+        let checkbox: JSX.Element | undefined;
 
         if (this.props.target instanceof Room) {
             title = _t("Share Room");
diff --git a/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx b/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx
index cf1bf354ce..9ef3e83ede 100644
--- a/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx
+++ b/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx
@@ -87,7 +87,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
     const validProxy = withValidation<undefined, { error?: Error }>({
         async deriveData({ value }): Promise<{ error?: Error }> {
             try {
-                await proxyHealthCheck(value, MatrixClientPeg.get().baseUrl);
+                await proxyHealthCheck(value!, MatrixClientPeg.get().baseUrl);
                 return {};
             } catch (error) {
                 return { error };
diff --git a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
index a4ab599563..9cffc20241 100644
--- a/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
+++ b/src/components/views/dialogs/security/AccessSecretStorageDialog.tsx
@@ -42,7 +42,7 @@ const VALIDATION_THROTTLE_MS = 200;
 export type KeyParams = { passphrase?: string; recoveryKey?: string };
 
 interface IProps {
-    keyInfo?: ISecretStorageKeyInfo;
+    keyInfo: ISecretStorageKeyInfo;
     checkPrivateKey: (k: KeyParams) => Promise<boolean>;
     onFinished(result?: false | KeyParams): void;
 }
diff --git a/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx b/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx
index bf4b170b25..31e85b1db4 100644
--- a/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx
+++ b/src/components/views/dialogs/security/RestoreKeyBackupDialog.tsx
@@ -136,6 +136,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
     };
 
     private onPassPhraseNext = async (): Promise<void> => {
+        if (!this.state.backupInfo) return;
         this.setState({
             loading: true,
             restoreError: null,
@@ -177,7 +178,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
     };
 
     private onRecoveryKeyNext = async (): Promise<void> => {
-        if (!this.state.recoveryKeyValid) return;
+        if (!this.state.recoveryKeyValid || !this.state.backupInfo) return;
 
         this.setState({
             loading: true,
@@ -228,6 +229,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
         try {
             // `accessSecretStorage` may prompt for storage access as needed.
             await accessSecretStorage(async (): Promise<void> => {
+                if (!this.state.backupInfo) return;
                 await MatrixClientPeg.get().restoreKeyBackupWithSecretStorage(
                     this.state.backupInfo,
                     undefined,
diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx
index a6393b7c82..34b10dc095 100644
--- a/src/components/views/rooms/EventTile.tsx
+++ b/src/components/views/rooms/EventTile.tsx
@@ -748,7 +748,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
             }
         }
 
-        if (MatrixClientPeg.get().isRoomEncrypted(ev.getRoomId())) {
+        if (MatrixClientPeg.get().isRoomEncrypted(ev.getRoomId()!)) {
             // else if room is encrypted
             // and event is being encrypted or is not_sent (Unknown Devices/Network Error)
             if (ev.status === EventStatus.ENCRYPTING) {
@@ -783,7 +783,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
         if (!this.props.showReactions || !this.props.getRelationsForEvent) {
             return null;
         }
-        const eventId = this.props.mxEvent.getId();
+        const eventId = this.props.mxEvent.getId()!;
         return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction") ?? null;
     };
 
@@ -801,7 +801,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
     };
 
     private onTimestampContextMenu = (ev: React.MouseEvent): void => {
-        this.showContextMenu(ev, this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId()));
+        this.showContextMenu(ev, this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId()!));
     };
 
     private showContextMenu(ev: React.MouseEvent, permalink?: string): void {
@@ -974,7 +974,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
 
         let permalink = "#";
         if (this.props.permalinkCreator) {
-            permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId());
+            permalink = this.props.permalinkCreator.forEvent(this.props.mxEvent.getId()!);
         }
 
         // we can't use local echoes as scroll tokens, because their event IDs change.
diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.tsx b/src/components/views/rooms/ThirdPartyMemberInfo.tsx
index 917e8d6ce3..233b1452f7 100644
--- a/src/components/views/rooms/ThirdPartyMemberInfo.tsx
+++ b/src/components/views/rooms/ThirdPartyMemberInfo.tsx
@@ -60,12 +60,12 @@ export default class ThirdPartyMemberInfo extends React.Component<IProps, IState
         const sender = this.room?.getMember(this.props.event.getSender());
 
         this.state = {
-            stateKey: this.props.event.getStateKey(),
-            roomId: this.props.event.getRoomId(),
+            stateKey: this.props.event.getStateKey()!,
+            roomId: this.props.event.getRoomId()!,
             displayName: this.props.event.getContent().display_name,
             invited: true,
             canKick: me ? me.powerLevel > kickLevel : false,
-            senderName: sender ? sender.name : this.props.event.getSender(),
+            senderName: sender?.name ?? this.props.event.getSender(),
         };
     }
 
diff --git a/src/components/views/settings/SecureBackupPanel.tsx b/src/components/views/settings/SecureBackupPanel.tsx
index da67d6a919..1df87008c7 100644
--- a/src/components/views/settings/SecureBackupPanel.tsx
+++ b/src/components/views/settings/SecureBackupPanel.tsx
@@ -123,7 +123,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
         this.getUpdatedDiagnostics();
         try {
             const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
-            const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo);
+            const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo!);
             if (this.unmounted) return;
             this.setState({
                 loading: false,
@@ -285,7 +285,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
                 );
             }
 
-            let backupSigStatuses: React.ReactNode = backupSigStatus.sigs.map((sig, i) => {
+            let backupSigStatuses: React.ReactNode = backupSigStatus?.sigs.map((sig, i) => {
                 const deviceName = sig.device ? sig.device.getDisplayName() || sig.device.deviceId : null;
                 const validity = (sub: string): JSX.Element => (
                     <span className={sig.valid ? "mx_SecureBackupPanel_sigValid" : "mx_SecureBackupPanel_sigInvalid"}>
diff --git a/src/components/views/settings/devices/deleteDevices.tsx b/src/components/views/settings/devices/deleteDevices.tsx
index c02c29f2cb..4b7af3119e 100644
--- a/src/components/views/settings/devices/deleteDevices.tsx
+++ b/src/components/views/settings/devices/deleteDevices.tsx
@@ -15,7 +15,7 @@ limitations under the License.
 */
 
 import { MatrixClient } from "matrix-js-sdk/src/matrix";
-import { IAuthData } from "matrix-js-sdk/src/interactive-auth";
+import { IAuthDict, IAuthData } from "matrix-js-sdk/src/interactive-auth";
 
 import { _t } from "../../../../languageHandler";
 import Modal from "../../../../Modal";
@@ -25,8 +25,8 @@ import InteractiveAuthDialog from "../../dialogs/InteractiveAuthDialog";
 
 const makeDeleteRequest =
     (matrixClient: MatrixClient, deviceIds: string[]) =>
-    async (auth?: IAuthData): Promise<IAuthData> => {
-        return matrixClient.deleteMultipleDevices(deviceIds, auth);
+    async (auth: IAuthDict | null): Promise<IAuthData> => {
+        return matrixClient.deleteMultipleDevices(deviceIds, auth ?? undefined);
     };
 
 export const deleteDevicesWithInteractiveAuth = async (
@@ -38,7 +38,7 @@ export const deleteDevicesWithInteractiveAuth = async (
         return;
     }
     try {
-        await makeDeleteRequest(matrixClient, deviceIds)();
+        await makeDeleteRequest(matrixClient, deviceIds)(null);
         // no interactive auth needed
         onFinished(true, undefined);
     } catch (error) {
diff --git a/src/components/views/settings/tabs/user/SessionManagerTab.tsx b/src/components/views/settings/tabs/user/SessionManagerTab.tsx
index 7305975be1..8162180b08 100644
--- a/src/components/views/settings/tabs/user/SessionManagerTab.tsx
+++ b/src/components/views/settings/tabs/user/SessionManagerTab.tsx
@@ -161,7 +161,7 @@ const SessionManagerTab: React.FC = () => {
     const shouldShowOtherSessions = otherSessionsCount > 0;
 
     const onVerifyCurrentDevice = (): void => {
-        Modal.createDialog(SetupEncryptionDialog as unknown as React.ComponentType, { onFinished: refreshDevices });
+        Modal.createDialog(SetupEncryptionDialog, { onFinished: refreshDevices });
     };
 
     const onTriggerDeviceVerification = useCallback(
diff --git a/src/dispatcher/payloads/ViewRoomPayload.ts b/src/dispatcher/payloads/ViewRoomPayload.ts
index a2f999dddd..eacd94d37e 100644
--- a/src/dispatcher/payloads/ViewRoomPayload.ts
+++ b/src/dispatcher/payloads/ViewRoomPayload.ts
@@ -22,17 +22,12 @@ import { Action } from "../actions";
 import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore";
 import { IOpts } from "../../createRoom";
 import { JoinRoomPayload } from "./JoinRoomPayload";
+import { AtLeastOne } from "../../@types/common";
 
 /* eslint-disable camelcase */
-export interface ViewRoomPayload extends Pick<ActionPayload, "action"> {
+interface BaseViewRoomPayload extends Pick<ActionPayload, "action"> {
     action: Action.ViewRoom;
 
-    // either or both of room_id or room_alias must be specified
-    // where possible, a room_id should be provided with a room_alias as it reduces
-    // the number of API calls required.
-    room_id?: string;
-    room_alias?: string;
-
     event_id?: string; // the event to ensure is in view if any
     highlighted?: boolean; // whether to highlight `event_id`
     scroll_into_view?: boolean; // whether to scroll `event_id` into view
@@ -57,4 +52,13 @@ export interface ViewRoomPayload extends Pick<ActionPayload, "action"> {
     metricsTrigger: ViewRoomEvent["trigger"];
     metricsViaKeyboard?: ViewRoomEvent["viaKeyboard"];
 }
+
+export type ViewRoomPayload = BaseViewRoomPayload &
+    AtLeastOne<{
+        // either or both of room_id or room_alias must be specified
+        // where possible, a room_id should be provided with a room_alias as it reduces
+        // the number of API calls required.
+        room_id?: string;
+        room_alias?: string;
+    }>;
 /* eslint-enable camelcase */
diff --git a/src/linkify-matrix.ts b/src/linkify-matrix.ts
index 6f921469c7..456298ce11 100644
--- a/src/linkify-matrix.ts
+++ b/src/linkify-matrix.ts
@@ -146,7 +146,7 @@ export const options: Opts = {
                         return {
                             // @ts-ignore see https://linkify.js.org/docs/options.html
                             click: function (e: MouseEvent) {
-                                onUserClick(e, permalink.userId);
+                                onUserClick(e, permalink.userId!);
                             },
                         };
                     } else {
diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts
index 9027e2870f..bd40aefe7a 100644
--- a/src/rageshake/submit-rageshake.ts
+++ b/src/rageshake/submit-rageshake.ts
@@ -280,8 +280,7 @@ function uint8ToString(buf: Uint8Array): string {
 }
 
 export async function submitFeedback(
-    endpoint: string,
-    label: string,
+    label: string | undefined,
     comment: string,
     canContact = false,
     extraData: Record<string, any> = {},
@@ -292,7 +291,7 @@ export async function submitFeedback(
     } catch (err) {} // PlatformPeg already logs this.
 
     const body = new FormData();
-    body.append("label", label);
+    if (label) body.append("label", label);
     body.append("text", comment);
     body.append("can_contact", canContact ? "yes" : "no");
 
diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx
index 641afe0e1b..68842d7dd6 100644
--- a/src/settings/Settings.tsx
+++ b/src/settings/Settings.tsx
@@ -215,7 +215,7 @@ export const SETTINGS: { [setting: string]: ISetting } = {
                 ),
             feedbackLabel: "video-room-feedback",
             feedbackSubheading: _td(
-                "Thank you for trying the beta, " + "please go into as much detail as you can so we can improve it.",
+                "Thank you for trying the beta, please go into as much detail as you can so we can improve it.",
             ),
             image: require("../../res/img/betas/video_rooms.png"),
             requiresRefresh: true,
diff --git a/src/stores/right-panel/RightPanelStore.ts b/src/stores/right-panel/RightPanelStore.ts
index caf85f28c8..e3e0183d40 100644
--- a/src/stores/right-panel/RightPanelStore.ts
+++ b/src/stores/right-panel/RightPanelStore.ts
@@ -308,7 +308,7 @@ export default class RightPanelStore extends ReadyWatchingStore {
         if (card.phase === RightPanelPhases.RoomMemberInfo && card.state) {
             // RightPanelPhases.RoomMemberInfo -> needs to be changed to RightPanelPhases.EncryptionPanel if there is a pending verification request
             const { member } = card.state;
-            const pendingRequest = pendingVerificationRequestForUser(member);
+            const pendingRequest = member ? pendingVerificationRequestForUser(member) : undefined;
             if (pendingRequest) {
                 return {
                     phase: RightPanelPhases.EncryptionPanel,
diff --git a/src/stores/right-panel/RightPanelStoreIPanelState.ts b/src/stores/right-panel/RightPanelStoreIPanelState.ts
index 826a767ee2..3599730e4f 100644
--- a/src/stores/right-panel/RightPanelStoreIPanelState.ts
+++ b/src/stores/right-panel/RightPanelStoreIPanelState.ts
@@ -71,8 +71,8 @@ interface IRightPanelForRoomStored {
     history: Array<IRightPanelCardStored>;
 }
 
-export function convertToStorePanel(cacheRoom: IRightPanelForRoom): IRightPanelForRoomStored {
-    if (!cacheRoom) return cacheRoom;
+export function convertToStorePanel(cacheRoom?: IRightPanelForRoom): IRightPanelForRoomStored | undefined {
+    if (!cacheRoom) return undefined;
     const storeHistory = [...cacheRoom.history].map((panelState) => convertCardToStore(panelState));
     return { isOpen: cacheRoom.isOpen, history: storeHistory };
 }
diff --git a/src/utils/UserInteractiveAuth.ts b/src/utils/UserInteractiveAuth.ts
index c953f0c9bd..2eed476c3c 100644
--- a/src/utils/UserInteractiveAuth.ts
+++ b/src/utils/UserInteractiveAuth.ts
@@ -14,13 +14,13 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import { IAuthData } from "matrix-js-sdk/src/interactive-auth";
+import { IAuthDict } from "matrix-js-sdk/src/interactive-auth";
 import { UIAResponse } from "matrix-js-sdk/src/@types/uia";
 
 import Modal from "../Modal";
 import InteractiveAuthDialog, { InteractiveAuthDialogProps } from "../components/views/dialogs/InteractiveAuthDialog";
 
-type FunctionWithUIA<R, A> = (auth?: IAuthData, ...args: A[]) => Promise<UIAResponse<R>>;
+type FunctionWithUIA<R, A> = (auth?: IAuthDict | null, ...args: A[]) => Promise<UIAResponse<R>>;
 
 export function wrapRequestWithDialog<R, A = any>(
     requestFunction: FunctionWithUIA<R, A>,
@@ -29,7 +29,7 @@ export function wrapRequestWithDialog<R, A = any>(
     return async function (...args): Promise<R> {
         return new Promise((resolve, reject) => {
             const boundFunction = requestFunction.bind(opts.matrixClient) as FunctionWithUIA<R, A>;
-            boundFunction(undefined, ...args)
+            boundFunction(null, ...args)
                 .then((res) => resolve(res as R))
                 .catch((error) => {
                     if (error.httpStatus !== 401 || !error.data?.flows) {
@@ -40,7 +40,7 @@ export function wrapRequestWithDialog<R, A = any>(
                     Modal.createDialog(InteractiveAuthDialog, {
                         ...opts,
                         authData: error.data,
-                        makeRequest: (authData?: IAuthData) => boundFunction(authData, ...args),
+                        makeRequest: (authData: IAuthDict | null) => boundFunction(authData, ...args),
                         onFinished: (success, result) => {
                             if (success) {
                                 resolve(result as R);
diff --git a/src/utils/localRoom/isLocalRoom.ts b/src/utils/localRoom/isLocalRoom.ts
index dffebd9777..c38ffa08e9 100644
--- a/src/utils/localRoom/isLocalRoom.ts
+++ b/src/utils/localRoom/isLocalRoom.ts
@@ -18,7 +18,7 @@ import { Room } from "matrix-js-sdk/src/matrix";
 
 import { LocalRoom, LOCAL_ROOM_ID_PREFIX } from "../../models/LocalRoom";
 
-export function isLocalRoom(roomOrID?: Room | string): boolean {
+export function isLocalRoom(roomOrID?: Room | string | null): boolean {
     if (typeof roomOrID === "string") {
         return roomOrID.startsWith(LOCAL_ROOM_ID_PREFIX);
     }
diff --git a/test/components/structures/LoggedInView-test.tsx b/test/components/structures/LoggedInView-test.tsx
index 5e389d65de..6c9a3abb65 100644
--- a/test/components/structures/LoggedInView-test.tsx
+++ b/test/components/structures/LoggedInView-test.tsx
@@ -54,6 +54,7 @@ describe("<LoggedInView />", () => {
             element_call: {},
         },
         currentRoomId: "",
+        currentUserId: "@bob:server",
     };
 
     const getComponent = (props = {}): RenderResult =>
diff --git a/test/components/structures/RoomSearchView-test.tsx b/test/components/structures/RoomSearchView-test.tsx
index e2a8360d46..199d1eecc5 100644
--- a/test/components/structures/RoomSearchView-test.tsx
+++ b/test/components/structures/RoomSearchView-test.tsx
@@ -529,25 +529,25 @@ describe("<RoomSearchView/>", () => {
         );
 
         const event1 = await screen.findByText("Room 1");
-        expect(event1.closest(".mx_EventTile_line").querySelector("a")).toHaveAttribute(
+        expect(event1.closest(".mx_EventTile_line")!.querySelector("a")).toHaveAttribute(
             "href",
             `https://matrix.to/#/${room.roomId}/$2`,
         );
 
         const event2 = await screen.findByText("Room 2");
-        expect(event2.closest(".mx_EventTile_line").querySelector("a")).toHaveAttribute(
+        expect(event2.closest(".mx_EventTile_line")!.querySelector("a")).toHaveAttribute(
             "href",
             `https://matrix.to/#/${room2.roomId}/$22`,
         );
 
         const event2Message2 = await screen.findByText("Room 2 message 2");
-        expect(event2Message2.closest(".mx_EventTile_line").querySelector("a")).toHaveAttribute(
+        expect(event2Message2.closest(".mx_EventTile_line")!.querySelector("a")).toHaveAttribute(
             "href",
             `https://matrix.to/#/${room2.roomId}/$23`,
         );
 
         const event3 = await screen.findByText("Room 3");
-        expect(event3.closest(".mx_EventTile_line").querySelector("a")).toHaveAttribute(
+        expect(event3.closest(".mx_EventTile_line")!.querySelector("a")).toHaveAttribute(
             "href",
             `https://matrix.to/#/${room3.roomId}/$32`,
         );
diff --git a/test/components/views/dialogs/AccessSecretStorageDialog-test.tsx b/test/components/views/dialogs/AccessSecretStorageDialog-test.tsx
index 7d66f39dfb..22bcde2557 100644
--- a/test/components/views/dialogs/AccessSecretStorageDialog-test.tsx
+++ b/test/components/views/dialogs/AccessSecretStorageDialog-test.tsx
@@ -30,6 +30,7 @@ describe("AccessSecretStorageDialog", () => {
     let mockClient: Mocked<MatrixClient>;
 
     const defaultProps: ComponentProps<typeof AccessSecretStorageDialog> = {
+        keyInfo: {} as any,
         onFinished: jest.fn(),
         checkPrivateKey: jest.fn(),
     };