From 56e4ae41f8ae06077e47bd674f500de677f28e4c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 17 Apr 2023 09:25:00 +0100 Subject: [PATCH] Conform more of the codebase to strictNullChecks (#10607) * Conform more of the codebase to `strictNullChecks` * Conform more of the codebase to `strictNullChecks` * Fix types * Conform more of the codebase to `strictNullChecks` * Conform more of the codebase to `strictNullChecks` --- src/components/structures/MessagePanel.tsx | 23 +++++++++++------- src/components/views/dialogs/ExportDialog.tsx | 4 ++-- .../views/dialogs/FeedbackDialog.tsx | 2 +- .../elements/IRCTimelineProfileResizer.tsx | 2 +- src/components/views/settings/BridgeTile.tsx | 2 +- .../views/settings/ChangePassword.tsx | 2 +- .../views/settings/CrossSigningPanel.tsx | 6 ++--- .../views/settings/DevicesPanelEntry.tsx | 6 ++--- .../views/settings/EventIndexPanel.tsx | 24 +++++++------------ .../views/settings/FontScalingPanel.tsx | 4 ++-- .../views/settings/JoinRuleSettings.tsx | 10 ++++---- .../views/settings/ProfileSettings.tsx | 11 ++++----- .../views/settings/SecureBackupPanel.tsx | 6 ++--- .../views/settings/devices/DeviceDetails.tsx | 6 ++--- .../views/settings/devices/DeviceTypeIcon.tsx | 4 ++-- .../views/spaces/SpaceCreateMenu.tsx | 4 ++-- src/components/views/spaces/SpacePanel.tsx | 4 ++-- src/stores/spaces/SpaceStore.ts | 5 ++++ .../structures/ThreadPanel-test.tsx | 14 +++++------ 19 files changed, 71 insertions(+), 68 deletions(-) diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index dd2ce65efb..ab01fad939 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -327,7 +327,11 @@ export default class MessagePanel extends React.Component { } private shouldHideSender(): boolean { - return this.props.room?.getInvitedAndJoinedMemberCount() <= 2 && this.props.layout === Layout.Bubble; + return ( + !!this.props.room && + this.props.room.getInvitedAndJoinedMemberCount() <= 2 && + this.props.layout === Layout.Bubble + ); } private calculateRoomMembersCount = (): void => { @@ -465,7 +469,7 @@ export default class MessagePanel extends React.Component { } } - if (MatrixClientPeg.get().isUserIgnored(mxEv.getSender())) { + if (MatrixClientPeg.get().isUserIgnored(mxEv.getSender()!)) { return false; // ignored = no show (only happens if the ignore happens after an event was received) } @@ -647,7 +651,7 @@ export default class MessagePanel extends React.Component { for (let i = 0; i < events.length; i++) { const eventAndShouldShow = events[i]; const { event, shouldShow } = eventAndShouldShow; - const eventId = event.getId(); + const eventId = event.getId()!; const last = event === lastShownEvent; const { nextEventAndShouldShow, nextTile } = this.getNextEventInfo(events, i); @@ -745,7 +749,7 @@ export default class MessagePanel extends React.Component { !wantsDateSeparator && shouldFormContinuation(prevEvent, mxEv, this.showHiddenEvents, this.context.timelineRenderingType); - const eventId = mxEv.getId(); + const eventId = mxEv.getId()!; const highlight = eventId === this.props.highlightedEventId; const readReceipts = this.readReceiptsByEvent.get(eventId); @@ -1075,7 +1079,7 @@ abstract class BaseGrouper { public readonly nextEventTile?: MatrixEvent | null, ) { this.readMarker = panel.readMarkerForEvent( - firstEventAndShouldShow.event.getId(), + firstEventAndShouldShow.event.getId()!, firstEventAndShouldShow.event === lastShownEvent, ); } @@ -1143,7 +1147,7 @@ class CreationGrouper extends BaseGrouper { public add({ event: ev, shouldShow }: EventAndShouldShow): void { const panel = this.panel; - this.readMarker = this.readMarker || panel.readMarkerForEvent(ev.getId(), ev === this.lastShownEvent); + this.readMarker = this.readMarker || panel.readMarkerForEvent(ev.getId()!, ev === this.lastShownEvent); if (!shouldShow) { return; } @@ -1295,7 +1299,7 @@ class MainGrouper extends BaseGrouper { // We can ignore any events that don't actually have a message to display if (!hasText(ev, this.panel.showHiddenEvents)) return; } - this.readMarker = this.readMarker || this.panel.readMarkerForEvent(ev.getId(), ev === this.lastShownEvent); + this.readMarker = this.readMarker || this.panel.readMarkerForEvent(ev.getId()!, ev === this.lastShownEvent); if (!this.panel.showHiddenEvents && !shouldShow) { // absorb hidden events to not split the summary return; @@ -1331,7 +1335,10 @@ class MainGrouper extends BaseGrouper { // This will prevent it from being re-created unnecessarily, and instead will allow new props to be provided. // In turn, the shouldComponentUpdate method on ELS can be used to prevent unnecessary renderings. const keyEvent = this.events.find((e) => this.panel.grouperKeyMap.get(e)); - const key = keyEvent ? this.panel.grouperKeyMap.get(keyEvent) : this.generateKey(); + const key = + keyEvent && this.panel.grouperKeyMap.has(keyEvent) + ? this.panel.grouperKeyMap.get(keyEvent)! + : this.generateKey(); if (!keyEvent) { // Populate the weak map with the key. // Note that we only set the key on the specific event it refers to, since this group might get diff --git a/src/components/views/dialogs/ExportDialog.tsx b/src/components/views/dialogs/ExportDialog.tsx index c3dbd1efef..ce5c4a9156 100644 --- a/src/components/views/dialogs/ExportDialog.tsx +++ b/src/components/views/dialogs/ExportDialog.tsx @@ -182,7 +182,7 @@ const ExportDialog: React.FC = ({ room, onFinished }) => { { key: "number", test: ({ value }) => { - const parsedSize = parseInt(value, 10); + const parsedSize = parseInt(value!, 10); return validateNumberInRange(1, 2000)(parsedSize); }, invalid: () => { @@ -218,7 +218,7 @@ const ExportDialog: React.FC = ({ room, onFinished }) => { { key: "number", test: ({ value }) => { - const parsedSize = parseInt(value, 10); + const parsedSize = parseInt(value!, 10); return validateNumberInRange(1, 10 ** 8)(parsedSize); }, invalid: () => { diff --git a/src/components/views/dialogs/FeedbackDialog.tsx b/src/components/views/dialogs/FeedbackDialog.tsx index 9996e65d2e..7ee24e05a4 100644 --- a/src/components/views/dialogs/FeedbackDialog.tsx +++ b/src/components/views/dialogs/FeedbackDialog.tsx @@ -38,7 +38,7 @@ interface IProps { } const FeedbackDialog: React.FC = (props: IProps) => { - const feedbackRef = useRef(); + const feedbackRef = useRef(null); const [comment, setComment] = useState(""); const [canContact, toggleCanContact] = useStateToggle(false); diff --git a/src/components/views/elements/IRCTimelineProfileResizer.tsx b/src/components/views/elements/IRCTimelineProfileResizer.tsx index 48bf1a188d..bae76d3f87 100644 --- a/src/components/views/elements/IRCTimelineProfileResizer.tsx +++ b/src/components/views/elements/IRCTimelineProfileResizer.tsx @@ -22,7 +22,7 @@ import { SettingLevel } from "../../../settings/SettingLevel"; interface IProps { // Current room - roomId: string; + roomId: string | null; minWidth: number; maxWidth: number; } diff --git a/src/components/views/settings/BridgeTile.tsx b/src/components/views/settings/BridgeTile.tsx index de40898f0d..673337f39b 100644 --- a/src/components/views/settings/BridgeTile.tsx +++ b/src/components/views/settings/BridgeTile.tsx @@ -97,7 +97,7 @@ export default class BridgeTile extends React.PureComponent { ), diff --git a/src/components/views/settings/ChangePassword.tsx b/src/components/views/settings/ChangePassword.tsx index 9978985ad8..8ea8326325 100644 --- a/src/components/views/settings/ChangePassword.tsx +++ b/src/components/views/settings/ChangePassword.tsx @@ -213,7 +213,7 @@ export default class ChangePassword extends React.Component { const modal = Modal.createDialog(SetEmailDialog, { title: _t("Do you want to set an email address?"), }); - return modal.finished.then(([confirmed]) => confirmed); + return modal.finished.then(([confirmed]) => !!confirmed); } private onExportE2eKeysClicked = (): void => { diff --git a/src/components/views/settings/CrossSigningPanel.tsx b/src/components/views/settings/CrossSigningPanel.tsx index 0ba8f580f5..e3f62d4ba2 100644 --- a/src/components/views/settings/CrossSigningPanel.tsx +++ b/src/components/views/settings/CrossSigningPanel.tsx @@ -94,9 +94,9 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> { const secretStorage = cli.crypto!.secretStorage; const crossSigningPublicKeysOnDevice = Boolean(crossSigning.getId()); const crossSigningPrivateKeysInStorage = Boolean(await crossSigning.isStoredInSecretStorage(secretStorage)); - const masterPrivateKeyCached = !!(pkCache && (await pkCache.getCrossSigningKeyCache("master"))); - const selfSigningPrivateKeyCached = !!(pkCache && (await pkCache.getCrossSigningKeyCache("self_signing"))); - const userSigningPrivateKeyCached = !!(pkCache && (await pkCache.getCrossSigningKeyCache("user_signing"))); + const masterPrivateKeyCached = !!(await pkCache?.getCrossSigningKeyCache?.("master")); + const selfSigningPrivateKeyCached = !!(await pkCache?.getCrossSigningKeyCache?.("self_signing")); + const userSigningPrivateKeyCached = !!(await pkCache?.getCrossSigningKeyCache?.("user_signing")); const homeserverSupportsCrossSigning = await cli.doesServerSupportUnstableFeature( "org.matrix.e2e_cross_signing", ); diff --git a/src/components/views/settings/DevicesPanelEntry.tsx b/src/components/views/settings/DevicesPanelEntry.tsx index 57f0bc24b2..95bcdb9e94 100644 --- a/src/components/views/settings/DevicesPanelEntry.tsx +++ b/src/components/views/settings/DevicesPanelEntry.tsx @@ -51,7 +51,7 @@ export default class DevicesPanelEntry extends React.Component { super(props); this.state = { renaming: false, - displayName: props.device.display_name, + displayName: props.device.display_name ?? "", }; } @@ -103,11 +103,11 @@ export default class DevicesPanelEntry extends React.Component { }); } else { const cli = MatrixClientPeg.get(); - const userId = cli.getUserId()!; + const userId = cli.getSafeUserId(); const verificationRequestPromise = cli.requestVerification(userId, [this.props.device.device_id]); Modal.createDialog(VerificationRequestDialog, { verificationRequestPromise, - member: cli.getUser(userId), + member: cli.getUser(userId) ?? undefined, onFinished: async (): Promise => { const request = await verificationRequestPromise; request.cancel(); diff --git a/src/components/views/settings/EventIndexPanel.tsx b/src/components/views/settings/EventIndexPanel.tsx index 8a1d93c98f..ea4c2b8c5c 100644 --- a/src/components/views/settings/EventIndexPanel.tsx +++ b/src/components/views/settings/EventIndexPanel.tsx @@ -26,7 +26,6 @@ import EventIndexPeg from "../../../indexing/EventIndexPeg"; import { SettingLevel } from "../../../settings/SettingLevel"; import SeshatResetDialog from "../dialogs/SeshatResetDialog"; import InlineSpinner from "../elements/InlineSpinner"; -import { IIndexStats } from "../../../indexing/BaseEventIndexManager"; interface IState { enabling: boolean; @@ -49,15 +48,9 @@ export default class EventIndexPanel extends React.Component<{}, IState> { public updateCurrentRoom = async (): Promise => { const eventIndex = EventIndexPeg.get(); - let stats: IIndexStats | undefined; - - try { - stats = await eventIndex?.getStats(); - } catch { - // This call may fail if sporadically, not a huge issue as we will - // try later again and probably succeed. - return; - } + const stats = await eventIndex?.getStats().catch(() => {}); + // This call may fail if sporadically, not a huge issue as we will try later again and probably succeed. + if (!stats) return; this.setState({ eventIndexSize: stats.size, @@ -88,14 +81,13 @@ export default class EventIndexPanel extends React.Component<{}, IState> { if (eventIndex !== null) { eventIndex.on("changedCheckpoint", this.updateCurrentRoom); - try { - const stats = await eventIndex.getStats(); + const stats = await eventIndex.getStats().catch(() => {}); + // This call may fail if sporadically, not a huge issue as we + // will try later again in the updateCurrentRoom call and + // probably succeed. + if (stats) { eventIndexSize = stats.size; roomCount = stats.roomCount; - } catch { - // This call may fail if sporadically, not a huge issue as we - // will try later again in the updateCurrentRoom call and - // probably succeed. } } diff --git a/src/components/views/settings/FontScalingPanel.tsx b/src/components/views/settings/FontScalingPanel.tsx index 49fe557671..03a8b963b8 100644 --- a/src/components/views/settings/FontScalingPanel.tsx +++ b/src/components/views/settings/FontScalingPanel.tsx @@ -83,7 +83,7 @@ export default class FontScalingPanel extends React.Component { }; private onValidateFontSize = async ({ value }: Pick): Promise => { - const parsedSize = parseFloat(value); + const parsedSize = parseFloat(value!); const min = FontWatcher.MIN_SIZE + FontWatcher.SIZE_DIFF; const max = FontWatcher.MAX_SIZE + FontWatcher.SIZE_DIFF; @@ -98,7 +98,7 @@ export default class FontScalingPanel extends React.Component { }; } - SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, parseInt(value, 10) - FontWatcher.SIZE_DIFF); + SettingsStore.setValue("baseFontSize", null, SettingLevel.DEVICE, parseInt(value!, 10) - FontWatcher.SIZE_DIFF); return { valid: true, feedback: _t("Use between %(min)s pt and %(max)s pt", { min, max }) }; }; diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index 5b1c5e0bbf..5706aa4dfa 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -61,7 +61,7 @@ const JoinRuleSettings: React.FC = ({ const disabled = !room.currentState.mayClientSendStateEvent(EventType.RoomJoinRules, cli); - const [content, setContent] = useLocalEcho( + const [content, setContent] = useLocalEcho( () => room.currentState.getStateEvents(EventType.RoomJoinRules, "")?.getContent(), (content) => cli.sendStateEvent(room.roomId, EventType.RoomJoinRules, content, ""), onError, @@ -70,10 +70,10 @@ const JoinRuleSettings: React.FC = ({ const { join_rule: joinRule = JoinRule.Invite } = content || {}; const restrictedAllowRoomIds = joinRule === JoinRule.Restricted - ? content.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id) + ? content?.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id) : undefined; - const editRestrictedRoomIds = async (): Promise => { + const editRestrictedRoomIds = async (): Promise => { let selected = restrictedAllowRoomIds; if (!selected?.length && SpaceStore.instance.activeSpaceRoom) { selected = [SpaceStore.instance.activeSpaceRoom.roomId]; @@ -207,7 +207,7 @@ const JoinRuleSettings: React.FC = ({ "Anyone in can find and join. You can select other spaces too.", {}, { - spaceName: () => {SpaceStore.instance.activeSpaceRoom.name}, + spaceName: () => {SpaceStore.instance.activeSpaceRoom!.name}, }, ); } else { @@ -229,7 +229,7 @@ const JoinRuleSettings: React.FC = ({ } const onChange = async (joinRule: JoinRule): Promise => { - const beforeJoinRule = content.join_rule; + const beforeJoinRule = content?.join_rule; let restrictedAllowRoomIds: string[] | undefined; if (joinRule === JoinRule.Restricted) { diff --git a/src/components/views/settings/ProfileSettings.tsx b/src/components/views/settings/ProfileSettings.tsx index 4a36b44973..d7fdd9c143 100644 --- a/src/components/views/settings/ProfileSettings.tsx +++ b/src/components/views/settings/ProfileSettings.tsx @@ -31,7 +31,6 @@ import { chromeFileInputFix } from "../../../utils/BrowserWorkarounds"; import PosthogTrackers from "../../../PosthogTrackers"; interface IState { - userId?: string; originalDisplayName: string; displayName: string; originalAvatarUrl: string | null; @@ -41,16 +40,16 @@ interface IState { } export default class ProfileSettings extends React.Component<{}, IState> { + private readonly userId: string; private avatarUpload: React.RefObject = createRef(); public constructor(props: {}) { super(props); - const client = MatrixClientPeg.get(); + this.userId = MatrixClientPeg.get().getSafeUserId(); let avatarUrl = OwnProfileStore.instance.avatarMxc; if (avatarUrl) avatarUrl = mediaFromMxc(avatarUrl).getSquareThumbnailHttp(96); this.state = { - userId: client.getUserId()!, originalDisplayName: OwnProfileStore.instance.displayName ?? "", displayName: OwnProfileStore.instance.displayName ?? "", originalAvatarUrl: avatarUrl, @@ -150,7 +149,7 @@ export default class ProfileSettings extends React.Component<{}, IState> { const reader = new FileReader(); reader.onload = (ev) => { this.setState({ - avatarUrl: ev.target?.result, + avatarUrl: ev.target?.result ?? undefined, avatarFile: file, enableProfileSave: true, }); @@ -159,7 +158,7 @@ export default class ProfileSettings extends React.Component<{}, IState> { }; public render(): React.ReactNode { - const userIdentifier = UserIdentifierCustomisations.getDisplayUserIdentifier(this.state.userId, { + const userIdentifier = UserIdentifierCustomisations.getDisplayUserIdentifier(this.userId, { withDisplayName: true, }); @@ -198,7 +197,7 @@ export default class ProfileSettings extends React.Component<{}, IState> { { return
{sigStatus}
; }); - if (backupSigStatus.sigs.length === 0) { + if (!backupSigStatus?.sigs?.length) { backupSigStatuses = _t("Backup is not signed by any of your sessions"); } - let trustedLocally; - if (backupSigStatus.trusted_locally) { + let trustedLocally: string | undefined; + if (backupSigStatus?.trusted_locally) { trustedLocally = _t("This backup is trusted because it has been restored on this session"); } diff --git a/src/components/views/settings/devices/DeviceDetails.tsx b/src/components/views/settings/devices/DeviceDetails.tsx index 56df38c6cf..fea916eb24 100644 --- a/src/components/views/settings/devices/DeviceDetails.tsx +++ b/src/components/views/settings/devices/DeviceDetails.tsx @@ -105,13 +105,13 @@ const DeviceDetails: React.FC = ({ const showPushNotificationSection = !!pusher || !!localNotificationSettings; - function isPushNotificationsEnabled(pusher: IPusher, notificationSettings: LocalNotificationSettings): boolean { - if (pusher) return pusher[PUSHER_ENABLED.name]; + function isPushNotificationsEnabled(pusher?: IPusher, notificationSettings?: LocalNotificationSettings): boolean { + if (pusher) return !!pusher[PUSHER_ENABLED.name]; if (localNotificationSettings) return !localNotificationSettings.is_silenced; return true; } - function isCheckboxDisabled(pusher: IPusher, notificationSettings: LocalNotificationSettings): boolean { + function isCheckboxDisabled(pusher?: IPusher, notificationSettings?: LocalNotificationSettings): boolean { if (localNotificationSettings) return false; if (pusher && !supportsMSC3881) return true; return false; diff --git a/src/components/views/settings/devices/DeviceTypeIcon.tsx b/src/components/views/settings/devices/DeviceTypeIcon.tsx index 06360c9a2f..c2fc0f5f2e 100644 --- a/src/components/views/settings/devices/DeviceTypeIcon.tsx +++ b/src/components/views/settings/devices/DeviceTypeIcon.tsx @@ -47,8 +47,8 @@ const deviceTypeLabel: Record = { }; export const DeviceTypeIcon: React.FC = ({ isVerified, isSelected, deviceType }) => { - const Icon = deviceTypeIcon[deviceType] || deviceTypeIcon[DeviceType.Unknown]; - const label = deviceTypeLabel[deviceType] || deviceTypeLabel[DeviceType.Unknown]; + const Icon = deviceTypeIcon[deviceType!] || deviceTypeIcon[DeviceType.Unknown]; + const label = deviceTypeLabel[deviceType!] || deviceTypeLabel[DeviceType.Unknown]; return (
(false); const [name, setName] = useState(""); - const spaceNameField = useRef(); + const spaceNameField = useRef(null); const [alias, setAlias] = useState(""); - const spaceAliasField = useRef(); + const spaceAliasField = useRef(null); const [avatar, setAvatar] = useState(undefined); const [topic, setTopic] = useState(""); diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index a138b909e0..8e95f29667 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -331,9 +331,9 @@ const InnerSpacePanel = React.memo( const SpacePanel: React.FC = () => { const [isPanelCollapsed, setPanelCollapsed] = useState(true); - const ref = useRef(); + const ref = useRef(null); useLayoutEffect(() => { - UIStore.instance.trackElementDimensions("SpacePanel", ref.current); + if (ref.current) UIStore.instance.trackElementDimensions("SpacePanel", ref.current); return () => UIStore.instance.stopTrackingElementDimensions("SpacePanel"); }, []); diff --git a/src/stores/spaces/SpaceStore.ts b/src/stores/spaces/SpaceStore.ts index 06dfa66971..5d94f1e3b0 100644 --- a/src/stores/spaces/SpaceStore.ts +++ b/src/stores/spaces/SpaceStore.ts @@ -553,6 +553,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { }; private rebuildSpaceHierarchy = (): void => { + if (!this.matrixClient) return; const visibleSpaces = this.matrixClient .getVisibleRooms(this._msc3946ProcessDynamicPredecessor) .filter((r) => r.isSpaceRoom()); @@ -589,6 +590,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { }; private rebuildParentMap = (): void => { + if (!this.matrixClient) return; const joinedSpaces = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor).filter((r) => { return r.isSpaceRoom() && r.getMyMembership() === "join"; }); @@ -624,6 +626,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { }; private rebuildMetaSpaces = (): void => { + if (!this.matrixClient) return; const enabledMetaSpaces = new Set(this.enabledMetaSpaces); const visibleRooms = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor); @@ -658,6 +661,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { }; private updateNotificationStates = (spaces?: SpaceKey[]): void => { + if (!this.matrixClient) return; const enabledMetaSpaces = new Set(this.enabledMetaSpaces); const visibleRooms = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor); @@ -745,6 +749,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { }; private onRoomsUpdate = (): void => { + if (!this.matrixClient) return; const visibleRooms = this.matrixClient.getVisibleRooms(this._msc3946ProcessDynamicPredecessor); const prevRoomsBySpace = this.roomIdsBySpace; diff --git a/test/components/structures/ThreadPanel-test.tsx b/test/components/structures/ThreadPanel-test.tsx index a851d0cf9b..b6cf843147 100644 --- a/test/components/structures/ThreadPanel-test.tsx +++ b/test/components/structures/ThreadPanel-test.tsx @@ -133,7 +133,7 @@ describe("ThreadPanel", () => { jest.spyOn(mockClient, "getRoom").mockReturnValue(room); await room.createThreadsTimelineSets(); const [allThreads, myThreads] = room.threadsTimelineSets; - jest.spyOn(room, "createThreadsTimelineSets").mockReturnValue(Promise.resolve([allThreads, myThreads])); + jest.spyOn(room, "createThreadsTimelineSets").mockReturnValue(Promise.resolve([allThreads!, myThreads!])); }); function toggleThreadFilter(container: HTMLElement, newFilter: ThreadFilterType) { @@ -195,11 +195,11 @@ describe("ThreadPanel", () => { return event ? Promise.resolve(event) : Promise.reject(); }); const [allThreads, myThreads] = room.threadsTimelineSets; - allThreads.addLiveEvent(otherThread.rootEvent); - allThreads.addLiveEvent(mixedThread.rootEvent); - allThreads.addLiveEvent(ownThread.rootEvent); - myThreads.addLiveEvent(mixedThread.rootEvent); - myThreads.addLiveEvent(ownThread.rootEvent); + allThreads!.addLiveEvent(otherThread.rootEvent); + allThreads!.addLiveEvent(mixedThread.rootEvent); + allThreads!.addLiveEvent(ownThread.rootEvent); + myThreads!.addLiveEvent(mixedThread.rootEvent); + myThreads!.addLiveEvent(ownThread.rootEvent); let events: EventData[] = []; const renderResult = render(); @@ -245,7 +245,7 @@ describe("ThreadPanel", () => { return event ? Promise.resolve(event) : Promise.reject(); }); const [allThreads] = room.threadsTimelineSets; - allThreads.addLiveEvent(otherThread.rootEvent); + allThreads!.addLiveEvent(otherThread.rootEvent); let events: EventData[] = []; const renderResult = render();