From cfd48b36aa0ca210157ea44a93d6d5f1963062a8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 7 Jul 2023 14:46:12 +0100 Subject: [PATCH] Enable strictPropertyInitialization (#11203) --- .github/workflows/static_analysis.yaml | 40 ------------------- src/Lifecycle.ts | 2 +- src/PosthogAnalytics.ts | 2 +- src/components/views/auth/CaptchaForm.tsx | 2 +- .../auth/InteractiveAuthEntryComponents.tsx | 2 +- .../views/beacon/BeaconViewDialog.tsx | 6 ++- .../dialogs/AddExistingToSpaceDialog.tsx | 8 ++-- .../views/dialogs/BugReportDialog.tsx | 2 +- .../dialogs/SessionRestoreErrorDialog.tsx | 2 +- .../dialogs/SlidingSyncOptionsDialog.tsx | 6 +-- .../views/dialogs/devtools/Event.tsx | 2 +- .../dialogs/devtools/SettingExplorer.tsx | 2 +- .../security/CreateCrossSigningDialog.tsx | 8 ++-- .../security/RestoreKeyBackupDialog.tsx | 10 +++-- .../views/directory/NetworkDropdown.tsx | 6 +-- .../views/elements/EditableTextContainer.tsx | 2 +- src/components/views/messages/MAudioBody.tsx | 6 +-- src/components/views/messages/MImageBody.tsx | 2 +- .../views/settings/CrossSigningPanel.tsx | 10 +++-- .../views/settings/EventIndexPanel.tsx | 6 ++- .../views/settings/JoinRuleSettings.tsx | 2 +- .../views/settings/SecureBackupPanel.tsx | 16 ++++---- src/customisations/Security.ts | 2 +- src/dispatcher/payloads/UploadPayload.ts | 2 +- src/hooks/useLocalEcho.ts | 2 +- src/i18n/strings/en_EN.json | 2 +- src/indexing/EventIndexPeg.ts | 2 +- src/sentry.ts | 2 +- src/stores/OwnBeaconStore.ts | 19 +++++---- src/stores/RoomViewStore.tsx | 2 +- src/utils/AutoDiscoveryUtils.tsx | 10 ++--- src/utils/IdentityServerUtils.ts | 2 +- src/utils/beacon/geolocation.ts | 6 +-- src/utils/leave-behaviour.ts | 4 +- src/utils/location/useMap.ts | 5 ++- .../views/settings/EventIndexPanel-test.tsx | 2 +- test/stores/OwnBeaconStore-test.ts | 5 ++- tsconfig.json | 3 +- 38 files changed, 97 insertions(+), 117 deletions(-) diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml index c4bf0ef3be..43c2e408b6 100644 --- a/.github/workflows/static_analysis.yaml +++ b/.github/workflows/static_analysis.yaml @@ -43,46 +43,6 @@ jobs: - name: Typecheck (release mode) run: "yarn run lint:types" - tsc-strict: - name: Typescript Strict Error Checker - if: github.event_name == 'pull_request' - runs-on: ubuntu-latest - permissions: - pull-requests: read - checks: write - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - - - name: Install Deps - run: "scripts/ci/layered.sh" - - - name: Get diff lines - id: diff - uses: Equip-Collaboration/diff-line-numbers@e752977e2cb4207d671bb9e4dad18c07c1b73d52 # v1.1.0 - with: - include: '["\\.tsx?$"]' - - - name: Detecting files changed - id: files - uses: futuratrepadeira/changed-files@0239328a3a6268aad16af7c3e4efc78e32d6c0f0 # v4.0.1 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - pattern: '^.*\.tsx?$' - - - uses: t3chguy/typescript-check-action@main - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - use-check: false - check-fail-mode: added - output-behaviour: annotate - ts-extra-args: "--strict --noImplicitAny" - files-changed: ${{ steps.files.outputs.files_updated }} - files-added: ${{ steps.files.outputs.files_created }} - files-deleted: ${{ steps.files.outputs.files_deleted }} - line-numbers: ${{ steps.diff.outputs.lineNumbers }} - i18n_lint: name: "i18n Check" uses: matrix-org/matrix-react-sdk/.github/workflows/i18n_check.yml@develop diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 5fb627ec70..6d1be2240d 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -497,7 +497,7 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }): } } -async function handleLoadSessionFailure(e: Error): Promise { +async function handleLoadSessionFailure(e: unknown): Promise { logger.error("Unable to load session", e); const modal = Modal.createDialog(SessionRestoreErrorDialog, { diff --git a/src/PosthogAnalytics.ts b/src/PosthogAnalytics.ts index 9e76ebb097..995559b1ee 100644 --- a/src/PosthogAnalytics.ts +++ b/src/PosthogAnalytics.ts @@ -324,7 +324,7 @@ export class PosthogAnalytics { } catch (e) { // The above could fail due to network requests, but not essential to starting the application, // so swallow it. - logger.log("Unable to identify user for tracking" + e.toString()); + logger.log("Unable to identify user for tracking", e); } } } diff --git a/src/components/views/auth/CaptchaForm.tsx b/src/components/views/auth/CaptchaForm.tsx index ec67acb5d0..97d2463e29 100644 --- a/src/components/views/auth/CaptchaForm.tsx +++ b/src/components/views/auth/CaptchaForm.tsx @@ -117,7 +117,7 @@ export default class CaptchaForm extends React.Component = ({ initialFocusedBeacon, roomId, matr const { bounds, centerGeoUri } = useMapPosition(liveBeacons, focusedBeaconState); - const [mapDisplayError, setMapDisplayError] = useState(); + const [mapDisplayError, setMapDisplayError] = useState(); // automatically open the sidebar if there is no map to see useEffect(() => { @@ -156,7 +156,9 @@ const BeaconViewDialog: React.FC = ({ initialFocusedBeacon, roomId, matr )} )} - {mapDisplayError && } + {mapDisplayError instanceof Error && ( + + )} {!centerGeoUri && !mapDisplayError && ( {_t("No live locations")} diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index 3910449833..38b31edd25 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -154,7 +154,7 @@ export const AddExistingToSpace: React.FC = ({ const [selectedToAdd, setSelectedToAdd] = useState(new Set()); const [progress, setProgress] = useState(null); - const [error, setError] = useState(null); + const [error, setError] = useState(false); const [query, setQuery] = useState(""); const lcQuery = query.toLowerCase().trim(); @@ -196,10 +196,10 @@ export const AddExistingToSpace: React.FC = ({ }, [visibleRooms, space, lcQuery, existingRoomsSet, existingSubspacesSet]); const addRooms = async (): Promise => { - setError(null); + setError(false); setProgress(0); - let error: Error | undefined; + let error = false; for (const room of selectedToAdd) { const via = calculateRoomVia(room); @@ -216,7 +216,7 @@ export const AddExistingToSpace: React.FC = ({ setProgress((i) => (i ?? 0) + 1); } catch (e) { logger.error("Failed to add rooms to space", e); - error = e; + error = true; break; } } diff --git a/src/components/views/dialogs/BugReportDialog.tsx b/src/components/views/dialogs/BugReportDialog.tsx index 6bf30b2914..b5db06ecdd 100644 --- a/src/components/views/dialogs/BugReportDialog.tsx +++ b/src/components/views/dialogs/BugReportDialog.tsx @@ -37,7 +37,7 @@ interface IProps { onFinished: (success: boolean) => void; initialText?: string; label?: string; - error?: Error; + error?: unknown; } interface IState { diff --git a/src/components/views/dialogs/SessionRestoreErrorDialog.tsx b/src/components/views/dialogs/SessionRestoreErrorDialog.tsx index 9436b0ffad..0a82a0b8c7 100644 --- a/src/components/views/dialogs/SessionRestoreErrorDialog.tsx +++ b/src/components/views/dialogs/SessionRestoreErrorDialog.tsx @@ -27,7 +27,7 @@ import BaseDialog from "./BaseDialog"; import DialogButtons from "../elements/DialogButtons"; interface IProps { - error: Error; + error: unknown; onFinished(clear?: boolean): void; } diff --git a/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx b/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx index 3745700091..45ace9fa7d 100644 --- a/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx +++ b/src/components/views/dialogs/SlidingSyncOptionsDialog.tsx @@ -84,8 +84,8 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean): : _t("Your server lacks native support"); } - const validProxy = withValidation({ - async deriveData({ value }): Promise<{ error?: Error }> { + const validProxy = withValidation({ + async deriveData({ value }): Promise<{ error?: unknown }> { try { await proxyHealthCheck(value!, MatrixClientPeg.safeGet().baseUrl); return {}; @@ -104,7 +104,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean): final: true, test: async (_, { error }) => !error, valid: () => _t("Looks good"), - invalid: ({ error }) => error?.message ?? null, + invalid: ({ error }) => (error instanceof Error ? error.message : null), }, ], }); diff --git a/src/components/views/dialogs/devtools/Event.tsx b/src/components/views/dialogs/devtools/Event.tsx index 6d09357463..4fa0403e9d 100644 --- a/src/components/views/dialogs/devtools/Event.tsx +++ b/src/components/views/dialogs/devtools/Event.tsx @@ -111,7 +111,7 @@ export const EventEditor: React.FC = ({ fieldDefs, defaultCon const json = JSON.parse(content); await onSend(fieldData, json); } catch (e) { - return _t("Failed to send event!") + ` (${e.toString()})`; + return _t("Failed to send event!") + (e instanceof Error ? ` (${e.message})` : ""); } return _t("Event sent!"); }; diff --git a/src/components/views/dialogs/devtools/SettingExplorer.tsx b/src/components/views/dialogs/devtools/SettingExplorer.tsx index 1650c7d0ad..06e1d7f2fc 100644 --- a/src/components/views/dialogs/devtools/SettingExplorer.tsx +++ b/src/components/views/dialogs/devtools/SettingExplorer.tsx @@ -125,7 +125,7 @@ const EditSetting: React.FC = ({ setting, onBack }) => { } onBack(); } catch (e) { - return _t("Failed to save settings.") + ` (${e.message})`; + return _t("Failed to save settings.") + (e instanceof Error ? ` (${e.message})` : ""); } }; diff --git a/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx b/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx index d27e7aec04..ce089b873e 100644 --- a/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx +++ b/src/components/views/dialogs/security/CreateCrossSigningDialog.tsx @@ -37,7 +37,7 @@ interface IProps { } interface IState { - error: Error | null; + error: boolean; canUploadKeysWithPasswordOnly: boolean | null; accountPassword: string; } @@ -52,7 +52,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent => { this.setState({ - error: null, + error: false, }); try { @@ -161,7 +161,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent | null; loading: boolean; loadError: boolean | null; - restoreError: { - errcode: string; - } | null; + restoreError: unknown | null; recoveryKey: string; recoverInfo: IKeyBackupRestoreResult | null; recoveryKeyValid: boolean; @@ -343,7 +342,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent({ - deriveData: async ({ value }): Promise<{ error?: MatrixError }> => { +const validServer = withValidation({ + deriveData: async ({ value }): Promise<{ error?: unknown }> => { try { // check if we can successfully load this server's room directory await MatrixClientPeg.safeGet().publicRooms({ @@ -63,7 +63,7 @@ const validServer = withValidation({ test: async (_, { error }) => !error, valid: () => _t("Looks good"), invalid: ({ error }) => - error?.errcode === "M_FORBIDDEN" + error instanceof MatrixError && error.errcode === "M_FORBIDDEN" ? _t("You are not allowed to view this server's rooms list") : _t("Can't find this server or its room list"), }, diff --git a/src/components/views/elements/EditableTextContainer.tsx b/src/components/views/elements/EditableTextContainer.tsx index e2f4298062..c6df20549a 100644 --- a/src/components/views/elements/EditableTextContainer.tsx +++ b/src/components/views/elements/EditableTextContainer.tsx @@ -91,7 +91,7 @@ export default class EditableTextContainer extends React.Component const blob = await this.props.mediaEventHelper!.sourceBlob.value; buffer = await blob.arrayBuffer(); } catch (e) { - this.setState({ error: e }); + this.setState({ error: true }); logger.warn("Unable to decrypt audio message", e); return; // stop processing the audio file } } catch (e) { - this.setState({ error: e }); + this.setState({ error: true }); logger.warn("Unable to decrypt/download audio message", e); return; // stop processing the audio file } diff --git a/src/components/views/messages/MImageBody.tsx b/src/components/views/messages/MImageBody.tsx index d2f9fde030..91df9c87ac 100644 --- a/src/components/views/messages/MImageBody.tsx +++ b/src/components/views/messages/MImageBody.tsx @@ -50,7 +50,7 @@ interface IState { contentUrl: string | null; thumbUrl: string | null; isAnimated?: boolean; - error?: Error; + error?: unknown; imgError: boolean; imgLoaded: boolean; loadedImageDimensions?: { diff --git a/src/components/views/settings/CrossSigningPanel.tsx b/src/components/views/settings/CrossSigningPanel.tsx index ed53bd274c..217f4a4bf9 100644 --- a/src/components/views/settings/CrossSigningPanel.tsx +++ b/src/components/views/settings/CrossSigningPanel.tsx @@ -31,7 +31,7 @@ import AccessibleButton from "../elements/AccessibleButton"; import { SettingsSubsectionText } from "./shared/SettingsSubsection"; interface IState { - error?: Error; + error: boolean; crossSigningPublicKeysOnDevice?: boolean; crossSigningPrivateKeysInStorage?: boolean; masterPrivateKeyCached?: boolean; @@ -47,7 +47,9 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> { public constructor(props: {}) { super(props); - this.state = {}; + this.state = { + error: false, + }; } public componentDidMount(): void { @@ -125,7 +127,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> { * @param {bool} [forceReset] Bootstrap again even if keys already present */ private bootstrapCrossSigning = async ({ forceReset = false }): Promise => { - this.setState({ error: undefined }); + this.setState({ error: false }); try { const cli = MatrixClientPeg.safeGet(); await cli.bootstrapCrossSigning({ @@ -143,7 +145,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> { setupNewCrossSigning: forceReset, }); } catch (e) { - this.setState({ error: e }); + this.setState({ error: true }); logger.error("Error bootstrapping cross-signing", e); } if (this.unmounted) return; diff --git a/src/components/views/settings/EventIndexPanel.tsx b/src/components/views/settings/EventIndexPanel.tsx index eb33f6707d..e3cbed3679 100644 --- a/src/components/views/settings/EventIndexPanel.tsx +++ b/src/components/views/settings/EventIndexPanel.tsx @@ -239,7 +239,11 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
{_t("Advanced")} - {EventIndexPeg.error.message} + + {EventIndexPeg.error instanceof Error + ? EventIndexPeg.error.message + : _t("Unknown error")} +

{_t("Reset")} diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index 4aee5d74ca..864529e6b2 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -40,7 +40,7 @@ export interface JoinRuleSettingsProps { room: Room; promptUpgrade?: boolean; closeSettingsFn(): void; - onError(error: Error): void; + onError(error: unknown): void; beforeChange?(joinRule: JoinRule): Promise; // if returns false then aborts the change aliasWarning?: ReactNode; } diff --git a/src/components/views/settings/SecureBackupPanel.tsx b/src/components/views/settings/SecureBackupPanel.tsx index 80f756a30f..20bb0eb816 100644 --- a/src/components/views/settings/SecureBackupPanel.tsx +++ b/src/components/views/settings/SecureBackupPanel.tsx @@ -35,7 +35,7 @@ import { SettingsSubsectionText } from "./shared/SettingsSubsection"; interface IState { loading: boolean; - error: Error | null; + error: boolean; backupKeyStored: boolean | null; backupKeyCached: boolean | null; backupKeyWellFormed: boolean | null; @@ -54,7 +54,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { this.state = { loading: true, - error: null, + error: false, backupKeyStored: null, backupKeyCached: null, backupKeyWellFormed: null, @@ -103,7 +103,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { const keyBackupResult = await MatrixClientPeg.safeGet().checkKeyBackup(); this.setState({ loading: false, - error: null, + error: false, backupInfo: keyBackupResult?.backupInfo ?? null, backupSigStatus: keyBackupResult?.trustInfo ?? null, }); @@ -112,7 +112,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { if (this.unmounted) return; this.setState({ loading: false, - error: e, + error: true, backupInfo: null, backupSigStatus: null, }); @@ -128,7 +128,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { if (this.unmounted) return; this.setState({ loading: false, - error: null, + error: false, backupInfo, backupSigStatus, }); @@ -137,7 +137,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { if (this.unmounted) return; this.setState({ loading: false, - error: e, + error: true, backupInfo: null, backupSigStatus: null, }); @@ -209,13 +209,13 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> { }; private resetSecretStorage = async (): Promise => { - this.setState({ error: null }); + this.setState({ error: false }); try { await accessSecretStorage(async (): Promise => {}, /* forceReset = */ true); } catch (e) { logger.error("Error resetting secret storage", e); if (this.unmounted) return; - this.setState({ error: e }); + this.setState({ error: true }); } if (this.unmounted) return; this.loadBackupStatus(); diff --git a/src/customisations/Security.ts b/src/customisations/Security.ts index ae5a905f56..7c387ab225 100644 --- a/src/customisations/Security.ts +++ b/src/customisations/Security.ts @@ -42,7 +42,7 @@ function getSecretStorageKey(): Uint8Array | null { } /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ -function catchAccessSecretStorageError(e: Error): void { +function catchAccessSecretStorageError(e: unknown): void { // E.g. notify the user in some way } diff --git a/src/dispatcher/payloads/UploadPayload.ts b/src/dispatcher/payloads/UploadPayload.ts index ac47596e55..6a376f3486 100644 --- a/src/dispatcher/payloads/UploadPayload.ts +++ b/src/dispatcher/payloads/UploadPayload.ts @@ -39,7 +39,7 @@ export interface UploadErrorPayload extends UploadPayload { /** * An error to describe what went wrong with the upload. */ - error: Error; + error: unknown; } export interface UploadFinishedPayload extends UploadPayload { diff --git a/src/hooks/useLocalEcho.ts b/src/hooks/useLocalEcho.ts index f8956a1564..e1374c82c5 100644 --- a/src/hooks/useLocalEcho.ts +++ b/src/hooks/useLocalEcho.ts @@ -19,7 +19,7 @@ import { useState } from "react"; export const useLocalEcho = ( currentFactory: () => T, setterFn: (value: T) => Promise, - errorFn: (error: Error) => void, + errorFn: (error: unknown) => void, ): [value: T, handler: (value: T) => void] => { const [value, setValue] = useState(currentFactory); const handler = async (value: T): Promise => { diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 67d6493036..a6cba4d95f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1392,6 +1392,7 @@ "%(brand)s is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom %(brand)s Desktop with search components added.": "%(brand)s is missing some components required for securely caching encrypted messages locally. If you'd like to experiment with this feature, build a custom %(brand)s Desktop with search components added.", "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use %(brand)s Desktop for encrypted messages to appear in search results.": "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use %(brand)s Desktop for encrypted messages to appear in search results.", "Message search initialisation failed": "Message search initialisation failed", + "Unknown error": "Unknown error", "Hey you. You're the best!": "Hey you. You're the best!", "Size must be a number": "Size must be a number", "Custom font size can only be between %(min)s pt and %(max)s pt": "Custom font size can only be between %(min)s pt and %(max)s pt", @@ -2764,7 +2765,6 @@ "Remove %(count)s messages|one": "Remove 1 message", "Can't start voice message": "Can't start voice message", "You can't start a voice message as you are currently recording a live broadcast. Please end your live broadcast in order to start recording a voice message.": "You can't start a voice message as you are currently recording a live broadcast. Please end your live broadcast in order to start recording a voice message.", - "Unknown error": "Unknown error", "Unable to load commit detail: %(msg)s": "Unable to load commit detail: %(msg)s", "Unavailable": "Unavailable", "Changelog": "Changelog", diff --git a/src/indexing/EventIndexPeg.ts b/src/indexing/EventIndexPeg.ts index 11c4696fc1..5483310fab 100644 --- a/src/indexing/EventIndexPeg.ts +++ b/src/indexing/EventIndexPeg.ts @@ -37,7 +37,7 @@ const INDEX_VERSION = 1; */ export class EventIndexPeg { public index: EventIndex | null = null; - public error: Error | null = null; + public error: unknown; private _supportIsInstalled = false; diff --git a/src/sentry.ts b/src/sentry.ts index 909a21ed1c..b92e61ac8f 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -174,7 +174,7 @@ async function getContexts(): Promise { }; } -export async function sendSentryReport(userText: string, issueUrl: string, error?: Error): Promise { +export async function sendSentryReport(userText: string, issueUrl: string, error?: unknown): Promise { const sentryConfig = SdkConfig.getObject("sentry"); if (!sentryConfig) return; diff --git a/src/stores/OwnBeaconStore.ts b/src/stores/OwnBeaconStore.ts index 2509dc92a3..ad30cc2caa 100644 --- a/src/stores/OwnBeaconStore.ts +++ b/src/stores/OwnBeaconStore.ts @@ -105,14 +105,13 @@ export class OwnBeaconStore extends AsyncStoreWithClient { * Reset on successful publish of location */ public readonly beaconLocationPublishErrorCounts = new Map(); - public readonly beaconUpdateErrors = new Map(); + public readonly beaconUpdateErrors = new Map(); /** * ids of live beacons * ordered by creation time descending */ private liveBeaconIds: BeaconIdentifier[] = []; private locationInterval?: number; - private geolocationError?: GeolocationError; private clearPositionWatch?: ClearWatchCallback; /** * Track when the last position was published @@ -462,7 +461,11 @@ export class OwnBeaconStore extends AsyncStoreWithClient { try { this.clearPositionWatch = watchPosition(this.onWatchedPosition, this.onGeolocationError); } catch (error) { - this.onGeolocationError(error?.message); + if (error instanceof Error) { + this.onGeolocationError(error.message as GeolocationError); + } else { + console.error("Unexpected error", error); + } // don't set locationInterval if geolocation failed to setup return; } @@ -485,7 +488,6 @@ export class OwnBeaconStore extends AsyncStoreWithClient { clearInterval(this.locationInterval); this.locationInterval = undefined; this.lastPublishedPositionTimestamp = undefined; - this.geolocationError = undefined; if (this.clearPositionWatch) { this.clearPositionWatch(); @@ -507,8 +509,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient { }; private onGeolocationError = async (error: GeolocationError): Promise => { - this.geolocationError = error; - logger.error("Geolocation failed", this.geolocationError); + logger.error("Geolocation failed", error); // other errors are considered non-fatal // and self recovering @@ -531,7 +532,11 @@ export class OwnBeaconStore extends AsyncStoreWithClient { const position = await getCurrentPosition(); this.publishLocationToBeacons(mapGeolocationPositionToTimedGeo(position)); } catch (error) { - this.onGeolocationError(error?.message); + if (error instanceof Error) { + this.onGeolocationError(error.message as GeolocationError); + } else { + console.error("Unexpected error", error); + } } }; diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx index 1fa116094d..6d23220baf 100644 --- a/src/stores/RoomViewStore.tsx +++ b/src/stores/RoomViewStore.tsx @@ -497,7 +497,7 @@ export class RoomViewStore extends EventEmitter { action: Action.ViewRoomError, room_id: null, room_alias: payload.room_alias, - err, + err: err instanceof MatrixError ? err : undefined, }); return; } diff --git a/src/utils/AutoDiscoveryUtils.tsx b/src/utils/AutoDiscoveryUtils.tsx index 213bc5ea98..ba4b2415a6 100644 --- a/src/utils/AutoDiscoveryUtils.tsx +++ b/src/utils/AutoDiscoveryUtils.tsx @@ -43,11 +43,9 @@ export default class AutoDiscoveryUtils { * @param {string | Error} error The error to check * @returns {boolean} True if the error is a liveliness error. */ - public static isLivelinessError(error?: string | Error | null): boolean { + public static isLivelinessError(error: unknown): boolean { if (!error) return false; - return !!LIVELINESS_DISCOVERY_ERRORS.find((e) => - typeof error === "string" ? e === error : e === error.message, - ); + return !!LIVELINESS_DISCOVERY_ERRORS.find((e) => (error instanceof Error ? e === error.message : e === error)); } /** @@ -58,7 +56,7 @@ export default class AutoDiscoveryUtils { * implementation for known values. * @returns {*} The state for the component, given the error. */ - public static authComponentStateForError(err: string | Error | null, pageName = "login"): IAuthComponentState { + public static authComponentStateForError(err: unknown, pageName = "login"): IAuthComponentState { if (!err) { return { serverIsAlive: true, @@ -93,7 +91,7 @@ export default class AutoDiscoveryUtils { } let isFatalError = true; - const errorMessage = typeof err === "string" ? err : err.message; + const errorMessage = err instanceof Error ? err.message : err; if (errorMessage === AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER) { isFatalError = false; title = _t("Cannot reach identity server"); diff --git a/src/utils/IdentityServerUtils.ts b/src/utils/IdentityServerUtils.ts index d51d554ae4..ca5eeee178 100644 --- a/src/utils/IdentityServerUtils.ts +++ b/src/utils/IdentityServerUtils.ts @@ -40,7 +40,7 @@ export async function doesIdentityServerHaveTerms(matrixClient: MatrixClient, fu terms = await matrixClient.getTerms(SERVICE_TYPES.IS, fullUrl); } catch (e) { logger.error(e); - if (e.cors === "rejected" || (e instanceof HTTPError && e.httpStatus === 404)) { + if (e instanceof HTTPError && e.httpStatus === 404) { terms = null; } else { throw e; diff --git a/src/utils/beacon/geolocation.ts b/src/utils/beacon/geolocation.ts index 3a55bbe2dc..3b2d95bbdc 100644 --- a/src/utils/beacon/geolocation.ts +++ b/src/utils/beacon/geolocation.ts @@ -41,8 +41,8 @@ const isGeolocationPositionError = (error: unknown): error is GeolocationPositio /** * Maps GeolocationPositionError to our GeolocationError enum */ -export const mapGeolocationError = (error: GeolocationPositionError | Error): GeolocationError => { - logger.error("Geolocation failed", error?.message ?? error); +export const mapGeolocationError = (error: GeolocationPositionError | Error | unknown): GeolocationError => { + logger.error("Geolocation failed", error); if (isGeolocationPositionError(error)) { switch (error?.code) { @@ -55,7 +55,7 @@ export const mapGeolocationError = (error: GeolocationPositionError | Error): Ge default: return GeolocationError.Default; } - } else if (error.message === GeolocationError.Unavailable) { + } else if (error instanceof Error && error.message === GeolocationError.Unavailable) { return GeolocationError.Unavailable; } else { return GeolocationError.Default; diff --git a/src/utils/leave-behaviour.ts b/src/utils/leave-behaviour.ts index aa0004a627..799fbc8f36 100644 --- a/src/utils/leave-behaviour.ts +++ b/src/utils/leave-behaviour.ts @@ -105,8 +105,10 @@ export async function leaveRoomBehaviour( if (e instanceof MatrixError) { const message = e.data.error || _t("Unexpected server error trying to leave the room"); results[roomId] = Object.assign(new Error(message), { errcode: e.data.errcode, data: e.data }); + } else if (e instanceof Error) { + results[roomId] = e; } else { - results[roomId] = e || new Error("Failed to leave room for unknown causes"); + results[roomId] = new Error("Failed to leave room for unknown causes"); } } } else { diff --git a/src/utils/location/useMap.ts b/src/utils/location/useMap.ts index ba15fff308..f6fc0aa62d 100644 --- a/src/utils/location/useMap.ts +++ b/src/utils/location/useMap.ts @@ -41,7 +41,10 @@ export const useMap = ({ interactive, bodyId, onError }: UseMapProps): MapLibreM try { setMap(createMap(cli, !!interactive, bodyId, onError)); } catch (error) { - onError?.(error); + console.error("Error encountered in useMap", error); + if (error instanceof Error) { + onError?.(error); + } } return () => { if (map) { diff --git a/test/components/views/settings/EventIndexPanel-test.tsx b/test/components/views/settings/EventIndexPanel-test.tsx index 34956f44c1..065f6bebca 100644 --- a/test/components/views/settings/EventIndexPanel-test.tsx +++ b/test/components/views/settings/EventIndexPanel-test.tsx @@ -79,7 +79,7 @@ describe("", () => { jest.spyOn(SettingsStore, "getValueAt").mockReturnValue(true); // @ts-ignore private property - EventIndexPeg.error = { message: "Test error message" }; + EventIndexPeg.error = new Error("Test error message"); }); it("displays an error when no event index is found and enabling not in progress", () => { diff --git a/test/stores/OwnBeaconStore-test.ts b/test/stores/OwnBeaconStore-test.ts index d49e77008a..b3aae13166 100644 --- a/test/stores/OwnBeaconStore-test.ts +++ b/test/stores/OwnBeaconStore-test.ts @@ -1101,7 +1101,10 @@ describe("OwnBeaconStore", () => { // still sharing expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled(); expect(store.isMonitoringLiveLocation).toEqual(true); - expect(errorLogSpy).toHaveBeenCalledWith("Geolocation failed", "error message"); + expect(errorLogSpy).toHaveBeenCalledWith( + "Geolocation failed", + expect.objectContaining({ message: "error message" }), + ); }); it("publishes last known position after 30s of inactivity", async () => { diff --git a/tsconfig.json b/tsconfig.json index 556a11782d..814718f4d3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,8 +13,7 @@ "declaration": true, "jsx": "react", "lib": ["es2020", "dom", "dom.iterable"], - "strict": true, - "useUnknownInCatchVariables": false + "strict": true }, "include": [ "./node_modules/matrix-js-sdk/src/@types/*.d.ts",