From e1cad41bc3a06aa9176fb36efa1cf35cc9a2fe02 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 27 Jun 2023 17:39:56 +0100 Subject: [PATCH] Conform more of the codebase to strictNullChecks (#11134) --- src/MatrixClientPeg.ts | 8 +++----- src/Searching.ts | 10 ++++++---- src/components/structures/RoomView.tsx | 2 +- src/components/structures/ScrollPanel.tsx | 2 +- src/components/structures/TimelinePanel.tsx | 12 ++++++++---- src/components/views/messages/MjolnirBody.tsx | 4 ++-- src/components/views/rooms/RoomListHeader.tsx | 2 +- src/components/views/rooms/RoomPreviewBar.tsx | 2 +- src/stores/ModalWidgetStore.ts | 6 +----- src/stores/right-panel/RightPanelStore.ts | 4 ++-- src/utils/AutoDiscoveryUtils.tsx | 10 +++++++++- src/utils/DecryptFile.ts | 4 ++-- src/utils/StorageManager.ts | 2 +- src/utils/promise.ts | 4 ++-- test/utils/media/requestMediaPermissions-test.tsx | 8 ++++---- 15 files changed, 44 insertions(+), 36 deletions(-) diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index fabed4f61c3..21820f443a8 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -71,7 +71,7 @@ export interface IMatrixClientPeg { * * @returns {string} The homeserver name, if present. */ - getHomeserverName(): string | null; + getHomeserverName(): string; get(): MatrixClient | null; safeGet(): MatrixClient; @@ -358,10 +358,8 @@ class MatrixClientPegClass implements IMatrixClientPeg { }; } - public getHomeserverName(): string | null { - if (!this.matrixClient) return null; - - const matches = /^@[^:]+:(.+)$/.exec(this.matrixClient.getSafeUserId()); + public getHomeserverName(): string { + const matches = /^@[^:]+:(.+)$/.exec(this.safeGet().getSafeUserId()); if (matches === null || matches.length < 1) { throw new Error("Failed to derive homeserver name from user ID!"); } diff --git a/src/Searching.ts b/src/Searching.ts index 5ec128b0aa4..feb56cb3fe7 100644 --- a/src/Searching.ts +++ b/src/Searching.ts @@ -234,9 +234,11 @@ async function localPagination( ): Promise { const eventIndex = EventIndexPeg.get(); - const searchArgs = searchResult.seshatQuery; + if (!searchResult.seshatQuery) { + throw new Error("localSearchProcess must be called first"); + } - const localResult = await eventIndex!.search(searchArgs); + const localResult = await eventIndex!.search(searchResult.seshatQuery); if (!localResult) { throw new Error("Local search pagination failed"); } @@ -408,7 +410,7 @@ function combineEvents( ): IResultRoomEvents { const response = {} as IResultRoomEvents; - const cachedEvents = previousSearchResult.cachedEvents; + const cachedEvents = previousSearchResult.cachedEvents ?? []; let oldestEventFrom = previousSearchResult.oldestEventFrom; response.highlights = previousSearchResult.highlights; @@ -564,7 +566,7 @@ async function combinedPagination( // Fetch events from the server if we have a token for it and if it's the // local indexes turn or the local index has exhausted its results. if (searchResult.serverSideNextBatch && (oldestEventFrom === "local" || !searchArgs.next_batch)) { - const body = { body: searchResult._query, next_batch: searchResult.serverSideNextBatch }; + const body = { body: searchResult._query!, next_batch: searchResult.serverSideNextBatch }; serverSideResult = await client.search(body); } diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 898f9ff3bb4..2e2dc96b849 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -1704,7 +1704,7 @@ export class RoomView extends React.Component { } catch (error) { logger.error(`Failed to reject invite: ${error}`); - const msg = error.message ? error.message : JSON.stringify(error); + const msg = error instanceof Error ? error.message : JSON.stringify(error); Modal.createDialog(ErrorDialog, { title: _t("Failed to reject invite"), description: msg, diff --git a/src/components/structures/ScrollPanel.tsx b/src/components/structures/ScrollPanel.tsx index 0dc31665d2a..fa7da4156fd 100644 --- a/src/components/structures/ScrollPanel.tsx +++ b/src/components/structures/ScrollPanel.tsx @@ -700,7 +700,7 @@ export default class ScrollPanel extends React.Component { const trackedNode = this.getTrackedNode(); if (trackedNode) { const newBottomOffset = this.topFromBottom(trackedNode); - const bottomDiff = newBottomOffset - scrollState.bottomOffset; + const bottomDiff = newBottomOffset - (scrollState.bottomOffset ?? 0); this.bottomGrowth += bottomDiff; scrollState.bottomOffset = newBottomOffset; const newHeight = `${this.getListHeight()}px`; diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index 47e53f18ae2..d9dfa64493c 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -599,7 +599,7 @@ class TimelinePanel extends React.Component { if (!this.timelineWindow?.canPaginate(dir)) { debuglog("can't", dir, "paginate any further"); - this.setState({ [canPaginateKey]: false }); + this.setState({ [canPaginateKey]: false } as Pick); return Promise.resolve(false); } @@ -609,7 +609,7 @@ class TimelinePanel extends React.Component { } debuglog("Initiating paginate; backwards:" + backwards); - this.setState({ [paginatingKey]: true }); + this.setState({ [paginatingKey]: true } as Pick); return this.onPaginationRequest(this.timelineWindow, dir, PAGINATE_SIZE).then(async (r) => { if (this.unmounted) { @@ -2012,7 +2012,7 @@ class TimelinePanel extends React.Component { return receiptStore?.getEventReadUpTo(myUserId, ignoreSynthesized) ?? null; } - private setReadMarker(eventId: string | null, eventTs: number, inhibitSetState = false): void { + private setReadMarker(eventId: string | null, eventTs?: number, inhibitSetState = false): void { const roomId = this.props.timelineSet.room?.roomId; // don't update the state (and cause a re-render) if there is @@ -2023,7 +2023,11 @@ class TimelinePanel extends React.Component { // in order to later figure out if the read marker is // above or below the visible timeline, we stash the timestamp. - TimelinePanel.roomReadMarkerTsMap[roomId ?? ""] = eventTs; + if (eventTs !== undefined) { + TimelinePanel.roomReadMarkerTsMap[roomId ?? ""] = eventTs; + } else { + delete TimelinePanel.roomReadMarkerTsMap[roomId ?? ""]; + } if (inhibitSetState) { return; diff --git a/src/components/views/messages/MjolnirBody.tsx b/src/components/views/messages/MjolnirBody.tsx index 637802b4554..d37c21619b2 100644 --- a/src/components/views/messages/MjolnirBody.tsx +++ b/src/components/views/messages/MjolnirBody.tsx @@ -18,7 +18,7 @@ import React from "react"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { _t } from "../../../languageHandler"; -import AccessibleButton from "../elements/AccessibleButton"; +import AccessibleButton, { ButtonEvent } from "../elements/AccessibleButton"; interface IProps { mxEvent: MatrixEvent; @@ -26,7 +26,7 @@ interface IProps { } export default class MjolnirBody extends React.Component { - private onAllowClick = (e: React.MouseEvent): void => { + private onAllowClick = (e: ButtonEvent): void => { e.preventDefault(); e.stopPropagation(); diff --git a/src/components/views/rooms/RoomListHeader.tsx b/src/components/views/rooms/RoomListHeader.tsx index 1feb201e8a5..d1b7019b4a3 100644 --- a/src/components/views/rooms/RoomListHeader.tsx +++ b/src/components/views/rooms/RoomListHeader.tsx @@ -171,7 +171,7 @@ const RoomListHeader: React.FC = ({ onVisibilityChange }) => { contextMenu = ( diff --git a/src/components/views/rooms/RoomPreviewBar.tsx b/src/components/views/rooms/RoomPreviewBar.tsx index 96cbaa99173..ae06b482944 100644 --- a/src/components/views/rooms/RoomPreviewBar.tsx +++ b/src/components/views/rooms/RoomPreviewBar.tsx @@ -155,7 +155,7 @@ export default class RoomPreviewBar extends React.Component { ); this.setState({ invitedEmailMxid: result.mxid }); } catch (err) { - this.setState({ threePidFetchError: err }); + this.setState({ threePidFetchError: err as MatrixError }); } this.setState({ busy: false }); } diff --git a/src/stores/ModalWidgetStore.ts b/src/stores/ModalWidgetStore.ts index 1126df1f0e6..b561e183f9e 100644 --- a/src/stores/ModalWidgetStore.ts +++ b/src/stores/ModalWidgetStore.ts @@ -70,11 +70,7 @@ export class ModalWidgetStore extends AsyncStoreWithClient { widgetRoomId, sourceWidgetId: sourceWidget.id, onFinished: (success, data) => { - if (!success) { - this.closeModalWidget(sourceWidget, widgetRoomId, { "m.exited": true }); - } else { - this.closeModalWidget(sourceWidget, widgetRoomId, data); - } + this.closeModalWidget(sourceWidget, widgetRoomId, success && data ? data : { "m.exited": true }); this.openSourceWidgetId = null; this.openSourceWidgetRoomId = null; diff --git a/src/stores/right-panel/RightPanelStore.ts b/src/stores/right-panel/RightPanelStore.ts index 6d827aa171a..e0dc5b1fe35 100644 --- a/src/stores/right-panel/RightPanelStore.ts +++ b/src/stores/right-panel/RightPanelStore.ts @@ -363,7 +363,7 @@ export default class RightPanelStore extends ReadyWatchingStore { const panel = this.byRoom[this.viewedRoomId]; if (panel?.history) { panel.history = panel.history.filter( - (card) => + (card: IRightPanelCard) => card.phase != RightPanelPhases.RoomMemberInfo && card.phase != RightPanelPhases.Room3pidMemberInfo, ); @@ -371,7 +371,7 @@ export default class RightPanelStore extends ReadyWatchingStore { } // when we're switching to a room, clear out thread permalinks to not get you stuck in the middle of the thread // in order to fix https://github.com/matrix-org/matrix-react-sdk/pull/11011 - if (this.currentCard?.phase === RightPanelPhases.ThreadView) { + if (this.currentCard?.phase === RightPanelPhases.ThreadView && this.currentCard.state) { this.currentCard.state.initialEvent = undefined; this.currentCard.state.isInitialEventHighlighted = undefined; this.currentCard.state.initialEventScrollIntoView = undefined; diff --git a/src/utils/AutoDiscoveryUtils.tsx b/src/utils/AutoDiscoveryUtils.tsx index 96fe807a122..213bc5ea986 100644 --- a/src/utils/AutoDiscoveryUtils.tsx +++ b/src/utils/AutoDiscoveryUtils.tsx @@ -261,7 +261,15 @@ export default class AutoDiscoveryUtils { throw new UserFriendlyError("Unexpected error resolving homeserver configuration"); } - let delegatedAuthentication = undefined; + let delegatedAuthentication: + | { + authorizationEndpoint: string; + registrationEndpoint?: string; + tokenEndpoint: string; + account?: string; + issuer: string; + } + | undefined; if (discoveryResult[M_AUTHENTICATION.stable!]?.state === AutoDiscovery.SUCCESS) { const { authorizationEndpoint, registrationEndpoint, tokenEndpoint, account, issuer } = discoveryResult[ M_AUTHENTICATION.stable! diff --git a/src/utils/DecryptFile.ts b/src/utils/DecryptFile.ts index 506c40441f6..ed0f3953a38 100644 --- a/src/utils/DecryptFile.ts +++ b/src/utils/DecryptFile.ts @@ -60,7 +60,7 @@ export async function decryptFile(file?: IEncryptedFile, info?: IMediaEventInfo) } responseData = await response.arrayBuffer(); } catch (e) { - throw new DownloadError(e); + throw new DownloadError(e as Error); } try { @@ -77,6 +77,6 @@ export async function decryptFile(file?: IEncryptedFile, info?: IMediaEventInfo) return new Blob([dataArray], { type: mimetype }); } catch (e) { - throw new DecryptError(e); + throw new DecryptError(e as Error); } } diff --git a/src/utils/StorageManager.ts b/src/utils/StorageManager.ts index 6cab834855a..a7ed5d76aa8 100644 --- a/src/utils/StorageManager.ts +++ b/src/utils/StorageManager.ts @@ -36,7 +36,7 @@ function log(msg: string): void { logger.log(`StorageManager: ${msg}`); } -function error(msg: string, ...args: string[]): void { +function error(msg: string, ...args: any[]): void { logger.error(`StorageManager: ${msg}`, ...args); } diff --git a/src/utils/promise.ts b/src/utils/promise.ts index 1ae1018b0cb..2d4c3dc0110 100644 --- a/src/utils/promise.ts +++ b/src/utils/promise.ts @@ -33,14 +33,14 @@ export async function retry( num: number, predicate?: (e: E) => boolean, ): Promise { - let lastErr!: E; + let lastErr!: any; for (let i = 0; i < num; i++) { try { const v = await fn(); // If `await fn()` throws then we won't reach here return v; } catch (err) { - if (predicate && !predicate(err)) { + if (predicate && !predicate(err as E)) { throw err; } lastErr = err; diff --git a/test/utils/media/requestMediaPermissions-test.tsx b/test/utils/media/requestMediaPermissions-test.tsx index 0239e6c30e0..633f6ab9fac 100644 --- a/test/utils/media/requestMediaPermissions-test.tsx +++ b/test/utils/media/requestMediaPermissions-test.tsx @@ -41,7 +41,7 @@ describe("requestMediaPermissions", () => { describe("when an audio and video device is available", () => { beforeEach(() => { mocked(navigator.mediaDevices.getUserMedia).mockImplementation( - async ({ audio, video }): Promise => { + async ({ audio, video }: MediaStreamConstraints): Promise => { if (audio && video) return audioVideoStream; return audioStream; }, @@ -56,7 +56,7 @@ describe("requestMediaPermissions", () => { describe("when calling with video = false and an audio device is available", () => { beforeEach(() => { mocked(navigator.mediaDevices.getUserMedia).mockImplementation( - async ({ audio, video }): Promise => { + async ({ audio, video }: MediaStreamConstraints): Promise => { if (audio && !video) return audioStream; return audioVideoStream; }, @@ -72,7 +72,7 @@ describe("requestMediaPermissions", () => { beforeEach(() => { error.name = "NotFoundError"; mocked(navigator.mediaDevices.getUserMedia).mockImplementation( - async ({ audio, video }): Promise => { + async ({ audio, video }: MediaStreamConstraints): Promise => { if (audio && video) throw error; if (audio) return audioStream; return audioVideoStream; @@ -103,7 +103,7 @@ describe("requestMediaPermissions", () => { describe("when an Error is raised", () => { beforeEach(async () => { mocked(navigator.mediaDevices.getUserMedia).mockImplementation( - async ({ audio, video }): Promise => { + async ({ audio, video }: MediaStreamConstraints): Promise => { if (audio && video) throw error; return audioVideoStream; },