Enable strictPropertyInitialization (#11203)

This commit is contained in:
Michael Telatynski 2023-07-07 14:46:12 +01:00 committed by GitHub
parent 4207d182cd
commit cfd48b36aa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 97 additions and 117 deletions

View file

@ -43,46 +43,6 @@ jobs:
- name: Typecheck (release mode) - name: Typecheck (release mode)
run: "yarn run lint:types" 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: i18n_lint:
name: "i18n Check" name: "i18n Check"
uses: matrix-org/matrix-react-sdk/.github/workflows/i18n_check.yml@develop uses: matrix-org/matrix-react-sdk/.github/workflows/i18n_check.yml@develop

View file

@ -497,7 +497,7 @@ export async function restoreFromLocalStorage(opts?: { ignoreGuest?: boolean }):
} }
} }
async function handleLoadSessionFailure(e: Error): Promise<boolean> { async function handleLoadSessionFailure(e: unknown): Promise<boolean> {
logger.error("Unable to load session", e); logger.error("Unable to load session", e);
const modal = Modal.createDialog(SessionRestoreErrorDialog, { const modal = Modal.createDialog(SessionRestoreErrorDialog, {

View file

@ -324,7 +324,7 @@ export class PosthogAnalytics {
} catch (e) { } catch (e) {
// The above could fail due to network requests, but not essential to starting the application, // The above could fail due to network requests, but not essential to starting the application,
// so swallow it. // so swallow it.
logger.log("Unable to identify user for tracking" + e.toString()); logger.log("Unable to identify user for tracking", e);
} }
} }
} }

View file

@ -117,7 +117,7 @@ export default class CaptchaForm extends React.Component<ICaptchaFormProps, ICap
}); });
} catch (e) { } catch (e) {
this.setState({ this.setState({
errorText: e.toString(), errorText: e instanceof Error ? e.message : String(e),
}); });
} }
} }

View file

@ -647,7 +647,7 @@ export class MsisdnAuthEntry extends React.Component<IMsisdnAuthEntryProps, IMsi
}); });
} }
} catch (e) { } catch (e) {
this.props.fail(e); this.props.fail(e instanceof Error ? e : new Error("Failed to submit msisdn token"));
logger.log("Failed to submit msisdn token"); logger.log("Failed to submit msisdn token");
} }
}; };

View file

@ -112,7 +112,7 @@ const BeaconViewDialog: React.FC<IProps> = ({ initialFocusedBeacon, roomId, matr
const { bounds, centerGeoUri } = useMapPosition(liveBeacons, focusedBeaconState); const { bounds, centerGeoUri } = useMapPosition(liveBeacons, focusedBeaconState);
const [mapDisplayError, setMapDisplayError] = useState<Error>(); const [mapDisplayError, setMapDisplayError] = useState<unknown>();
// automatically open the sidebar if there is no map to see // automatically open the sidebar if there is no map to see
useEffect(() => { useEffect(() => {
@ -156,7 +156,9 @@ const BeaconViewDialog: React.FC<IProps> = ({ initialFocusedBeacon, roomId, matr
)} )}
</Map> </Map>
)} )}
{mapDisplayError && <MapError error={mapDisplayError.message as LocationShareError} isMinimised />} {mapDisplayError instanceof Error && (
<MapError error={mapDisplayError.message as LocationShareError} isMinimised />
)}
{!centerGeoUri && !mapDisplayError && ( {!centerGeoUri && !mapDisplayError && (
<MapFallback data-testid="beacon-view-dialog-map-fallback" className="mx_BeaconViewDialog_map"> <MapFallback data-testid="beacon-view-dialog-map-fallback" className="mx_BeaconViewDialog_map">
<span className="mx_BeaconViewDialog_mapFallbackMessage">{_t("No live locations")}</span> <span className="mx_BeaconViewDialog_mapFallbackMessage">{_t("No live locations")}</span>

View file

@ -154,7 +154,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>()); const [selectedToAdd, setSelectedToAdd] = useState(new Set<Room>());
const [progress, setProgress] = useState<number | null>(null); const [progress, setProgress] = useState<number | null>(null);
const [error, setError] = useState<Error | null>(null); const [error, setError] = useState(false);
const [query, setQuery] = useState(""); const [query, setQuery] = useState("");
const lcQuery = query.toLowerCase().trim(); const lcQuery = query.toLowerCase().trim();
@ -196,10 +196,10 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
}, [visibleRooms, space, lcQuery, existingRoomsSet, existingSubspacesSet]); }, [visibleRooms, space, lcQuery, existingRoomsSet, existingSubspacesSet]);
const addRooms = async (): Promise<void> => { const addRooms = async (): Promise<void> => {
setError(null); setError(false);
setProgress(0); setProgress(0);
let error: Error | undefined; let error = false;
for (const room of selectedToAdd) { for (const room of selectedToAdd) {
const via = calculateRoomVia(room); const via = calculateRoomVia(room);
@ -216,7 +216,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
setProgress((i) => (i ?? 0) + 1); setProgress((i) => (i ?? 0) + 1);
} catch (e) { } catch (e) {
logger.error("Failed to add rooms to space", e); logger.error("Failed to add rooms to space", e);
error = e; error = true;
break; break;
} }
} }

View file

@ -37,7 +37,7 @@ interface IProps {
onFinished: (success: boolean) => void; onFinished: (success: boolean) => void;
initialText?: string; initialText?: string;
label?: string; label?: string;
error?: Error; error?: unknown;
} }
interface IState { interface IState {

View file

@ -27,7 +27,7 @@ import BaseDialog from "./BaseDialog";
import DialogButtons from "../elements/DialogButtons"; import DialogButtons from "../elements/DialogButtons";
interface IProps { interface IProps {
error: Error; error: unknown;
onFinished(clear?: boolean): void; onFinished(clear?: boolean): void;
} }

View file

@ -84,8 +84,8 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
: _t("Your server lacks native support"); : _t("Your server lacks native support");
} }
const validProxy = withValidation<undefined, { error?: Error }>({ const validProxy = withValidation<undefined, { error?: unknown }>({
async deriveData({ value }): Promise<{ error?: Error }> { async deriveData({ value }): Promise<{ error?: unknown }> {
try { try {
await proxyHealthCheck(value!, MatrixClientPeg.safeGet().baseUrl); await proxyHealthCheck(value!, MatrixClientPeg.safeGet().baseUrl);
return {}; return {};
@ -104,7 +104,7 @@ export const SlidingSyncOptionsDialog: React.FC<{ onFinished(enabled: boolean):
final: true, final: true,
test: async (_, { error }) => !error, test: async (_, { error }) => !error,
valid: () => _t("Looks good"), valid: () => _t("Looks good"),
invalid: ({ error }) => error?.message ?? null, invalid: ({ error }) => (error instanceof Error ? error.message : null),
}, },
], ],
}); });

View file

@ -111,7 +111,7 @@ export const EventEditor: React.FC<IEventEditorProps> = ({ fieldDefs, defaultCon
const json = JSON.parse(content); const json = JSON.parse(content);
await onSend(fieldData, json); await onSend(fieldData, json);
} catch (e) { } 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!"); return _t("Event sent!");
}; };

View file

@ -125,7 +125,7 @@ const EditSetting: React.FC<IEditSettingProps> = ({ setting, onBack }) => {
} }
onBack(); onBack();
} catch (e) { } catch (e) {
return _t("Failed to save settings.") + ` (${e.message})`; return _t("Failed to save settings.") + (e instanceof Error ? ` (${e.message})` : "");
} }
}; };

View file

@ -37,7 +37,7 @@ interface IProps {
} }
interface IState { interface IState {
error: Error | null; error: boolean;
canUploadKeysWithPasswordOnly: boolean | null; canUploadKeysWithPasswordOnly: boolean | null;
accountPassword: string; accountPassword: string;
} }
@ -52,7 +52,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent<IProps
super(props); super(props);
this.state = { this.state = {
error: null, error: false,
// Does the server offer a UI auth flow with just m.login.password // Does the server offer a UI auth flow with just m.login.password
// for /keys/device_signing/upload? // for /keys/device_signing/upload?
// If we have an account password in memory, let's simplify and // If we have an account password in memory, let's simplify and
@ -145,7 +145,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent<IProps
private bootstrapCrossSigning = async (): Promise<void> => { private bootstrapCrossSigning = async (): Promise<void> => {
this.setState({ this.setState({
error: null, error: false,
}); });
try { try {
@ -161,7 +161,7 @@ export default class CreateCrossSigningDialog extends React.PureComponent<IProps
return; return;
} }
this.setState({ error: e }); this.setState({ error: true });
logger.error("Error bootstrapping cross-signing", e); logger.error("Error bootstrapping cross-signing", e);
} }
}; };

View file

@ -20,6 +20,7 @@ import { MatrixClient } from "matrix-js-sdk/src/client";
import { IKeyBackupInfo, IKeyBackupRestoreResult } from "matrix-js-sdk/src/crypto/keybackup"; import { IKeyBackupInfo, IKeyBackupRestoreResult } from "matrix-js-sdk/src/crypto/keybackup";
import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api"; import { ISecretStorageKeyInfo } from "matrix-js-sdk/src/crypto/api";
import { logger } from "matrix-js-sdk/src/logger"; import { logger } from "matrix-js-sdk/src/logger";
import { MatrixError } from "matrix-js-sdk/src/matrix";
import { MatrixClientPeg } from "../../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../../MatrixClientPeg";
import { _t } from "../../../../languageHandler"; import { _t } from "../../../../languageHandler";
@ -56,9 +57,7 @@ interface IState {
backupKeyStored: Record<string, ISecretStorageKeyInfo> | null; backupKeyStored: Record<string, ISecretStorageKeyInfo> | null;
loading: boolean; loading: boolean;
loadError: boolean | null; loadError: boolean | null;
restoreError: { restoreError: unknown | null;
errcode: string;
} | null;
recoveryKey: string; recoveryKey: string;
recoverInfo: IKeyBackupRestoreResult | null; recoverInfo: IKeyBackupRestoreResult | null;
recoveryKeyValid: boolean; recoveryKeyValid: boolean;
@ -343,7 +342,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
title = _t("Error"); title = _t("Error");
content = _t("Unable to load backup status"); content = _t("Unable to load backup status");
} else if (this.state.restoreError) { } else if (this.state.restoreError) {
if (this.state.restoreError.errcode === MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY) { if (
this.state.restoreError instanceof MatrixError &&
this.state.restoreError.errcode === MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY
) {
if (this.state.restoreType === RestoreType.RecoveryKey) { if (this.state.restoreType === RestoreType.RecoveryKey) {
title = _t("Security Key mismatch"); title = _t("Security Key mismatch");
content = ( content = (

View file

@ -38,8 +38,8 @@ export interface IPublicRoomDirectoryConfig {
instanceId?: string; instanceId?: string;
} }
const validServer = withValidation<undefined, { error?: MatrixError }>({ const validServer = withValidation<undefined, { error?: unknown }>({
deriveData: async ({ value }): Promise<{ error?: MatrixError }> => { deriveData: async ({ value }): Promise<{ error?: unknown }> => {
try { try {
// check if we can successfully load this server's room directory // check if we can successfully load this server's room directory
await MatrixClientPeg.safeGet().publicRooms({ await MatrixClientPeg.safeGet().publicRooms({
@ -63,7 +63,7 @@ const validServer = withValidation<undefined, { error?: MatrixError }>({
test: async (_, { error }) => !error, test: async (_, { error }) => !error,
valid: () => _t("Looks good"), valid: () => _t("Looks good"),
invalid: ({ error }) => 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("You are not allowed to view this server's rooms list")
: _t("Can't find this server or its room list"), : _t("Can't find this server or its room list"),
}, },

View file

@ -91,7 +91,7 @@ export default class EditableTextContainer extends React.Component<IProps, IStat
} catch (error) { } catch (error) {
if (this.unmounted) return; if (this.unmounted) return;
this.setState({ this.setState({
errorString: error.toString(), errorString: error instanceof Error ? error.message : String(error),
busy: false, busy: false,
}); });
} }

View file

@ -32,7 +32,7 @@ import RoomContext, { TimelineRenderingType } from "../../../contexts/RoomContex
import MediaProcessingError from "./shared/MediaProcessingError"; import MediaProcessingError from "./shared/MediaProcessingError";
interface IState { interface IState {
error?: Error; error?: boolean;
playback?: Playback; playback?: Playback;
} }
@ -54,12 +54,12 @@ export default class MAudioBody extends React.PureComponent<IBodyProps, IState>
const blob = await this.props.mediaEventHelper!.sourceBlob.value; const blob = await this.props.mediaEventHelper!.sourceBlob.value;
buffer = await blob.arrayBuffer(); buffer = await blob.arrayBuffer();
} catch (e) { } catch (e) {
this.setState({ error: e }); this.setState({ error: true });
logger.warn("Unable to decrypt audio message", e); logger.warn("Unable to decrypt audio message", e);
return; // stop processing the audio file return; // stop processing the audio file
} }
} catch (e) { } catch (e) {
this.setState({ error: e }); this.setState({ error: true });
logger.warn("Unable to decrypt/download audio message", e); logger.warn("Unable to decrypt/download audio message", e);
return; // stop processing the audio file return; // stop processing the audio file
} }

View file

@ -50,7 +50,7 @@ interface IState {
contentUrl: string | null; contentUrl: string | null;
thumbUrl: string | null; thumbUrl: string | null;
isAnimated?: boolean; isAnimated?: boolean;
error?: Error; error?: unknown;
imgError: boolean; imgError: boolean;
imgLoaded: boolean; imgLoaded: boolean;
loadedImageDimensions?: { loadedImageDimensions?: {

View file

@ -31,7 +31,7 @@ import AccessibleButton from "../elements/AccessibleButton";
import { SettingsSubsectionText } from "./shared/SettingsSubsection"; import { SettingsSubsectionText } from "./shared/SettingsSubsection";
interface IState { interface IState {
error?: Error; error: boolean;
crossSigningPublicKeysOnDevice?: boolean; crossSigningPublicKeysOnDevice?: boolean;
crossSigningPrivateKeysInStorage?: boolean; crossSigningPrivateKeysInStorage?: boolean;
masterPrivateKeyCached?: boolean; masterPrivateKeyCached?: boolean;
@ -47,7 +47,9 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
public constructor(props: {}) { public constructor(props: {}) {
super(props); super(props);
this.state = {}; this.state = {
error: false,
};
} }
public componentDidMount(): void { 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 * @param {bool} [forceReset] Bootstrap again even if keys already present
*/ */
private bootstrapCrossSigning = async ({ forceReset = false }): Promise<void> => { private bootstrapCrossSigning = async ({ forceReset = false }): Promise<void> => {
this.setState({ error: undefined }); this.setState({ error: false });
try { try {
const cli = MatrixClientPeg.safeGet(); const cli = MatrixClientPeg.safeGet();
await cli.bootstrapCrossSigning({ await cli.bootstrapCrossSigning({
@ -143,7 +145,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
setupNewCrossSigning: forceReset, setupNewCrossSigning: forceReset,
}); });
} catch (e) { } catch (e) {
this.setState({ error: e }); this.setState({ error: true });
logger.error("Error bootstrapping cross-signing", e); logger.error("Error bootstrapping cross-signing", e);
} }
if (this.unmounted) return; if (this.unmounted) return;

View file

@ -239,7 +239,11 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
<SettingsSubsectionText> <SettingsSubsectionText>
<details> <details>
<summary>{_t("Advanced")}</summary> <summary>{_t("Advanced")}</summary>
<code>{EventIndexPeg.error.message}</code> <code>
{EventIndexPeg.error instanceof Error
? EventIndexPeg.error.message
: _t("Unknown error")}
</code>
<p> <p>
<AccessibleButton key="delete" kind="danger" onClick={this.confirmEventStoreReset}> <AccessibleButton key="delete" kind="danger" onClick={this.confirmEventStoreReset}>
{_t("Reset")} {_t("Reset")}

View file

@ -40,7 +40,7 @@ export interface JoinRuleSettingsProps {
room: Room; room: Room;
promptUpgrade?: boolean; promptUpgrade?: boolean;
closeSettingsFn(): void; closeSettingsFn(): void;
onError(error: Error): void; onError(error: unknown): void;
beforeChange?(joinRule: JoinRule): Promise<boolean>; // if returns false then aborts the change beforeChange?(joinRule: JoinRule): Promise<boolean>; // if returns false then aborts the change
aliasWarning?: ReactNode; aliasWarning?: ReactNode;
} }

View file

@ -35,7 +35,7 @@ import { SettingsSubsectionText } from "./shared/SettingsSubsection";
interface IState { interface IState {
loading: boolean; loading: boolean;
error: Error | null; error: boolean;
backupKeyStored: boolean | null; backupKeyStored: boolean | null;
backupKeyCached: boolean | null; backupKeyCached: boolean | null;
backupKeyWellFormed: boolean | null; backupKeyWellFormed: boolean | null;
@ -54,7 +54,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
this.state = { this.state = {
loading: true, loading: true,
error: null, error: false,
backupKeyStored: null, backupKeyStored: null,
backupKeyCached: null, backupKeyCached: null,
backupKeyWellFormed: null, backupKeyWellFormed: null,
@ -103,7 +103,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
const keyBackupResult = await MatrixClientPeg.safeGet().checkKeyBackup(); const keyBackupResult = await MatrixClientPeg.safeGet().checkKeyBackup();
this.setState({ this.setState({
loading: false, loading: false,
error: null, error: false,
backupInfo: keyBackupResult?.backupInfo ?? null, backupInfo: keyBackupResult?.backupInfo ?? null,
backupSigStatus: keyBackupResult?.trustInfo ?? null, backupSigStatus: keyBackupResult?.trustInfo ?? null,
}); });
@ -112,7 +112,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
if (this.unmounted) return; if (this.unmounted) return;
this.setState({ this.setState({
loading: false, loading: false,
error: e, error: true,
backupInfo: null, backupInfo: null,
backupSigStatus: null, backupSigStatus: null,
}); });
@ -128,7 +128,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
if (this.unmounted) return; if (this.unmounted) return;
this.setState({ this.setState({
loading: false, loading: false,
error: null, error: false,
backupInfo, backupInfo,
backupSigStatus, backupSigStatus,
}); });
@ -137,7 +137,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
if (this.unmounted) return; if (this.unmounted) return;
this.setState({ this.setState({
loading: false, loading: false,
error: e, error: true,
backupInfo: null, backupInfo: null,
backupSigStatus: null, backupSigStatus: null,
}); });
@ -209,13 +209,13 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
}; };
private resetSecretStorage = async (): Promise<void> => { private resetSecretStorage = async (): Promise<void> => {
this.setState({ error: null }); this.setState({ error: false });
try { try {
await accessSecretStorage(async (): Promise<void> => {}, /* forceReset = */ true); await accessSecretStorage(async (): Promise<void> => {}, /* forceReset = */ true);
} catch (e) { } catch (e) {
logger.error("Error resetting secret storage", e); logger.error("Error resetting secret storage", e);
if (this.unmounted) return; if (this.unmounted) return;
this.setState({ error: e }); this.setState({ error: true });
} }
if (this.unmounted) return; if (this.unmounted) return;
this.loadBackupStatus(); this.loadBackupStatus();

View file

@ -42,7 +42,7 @@ function getSecretStorageKey(): Uint8Array | null {
} }
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */ /* 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 // E.g. notify the user in some way
} }

View file

@ -39,7 +39,7 @@ export interface UploadErrorPayload extends UploadPayload {
/** /**
* An error to describe what went wrong with the upload. * An error to describe what went wrong with the upload.
*/ */
error: Error; error: unknown;
} }
export interface UploadFinishedPayload extends UploadPayload { export interface UploadFinishedPayload extends UploadPayload {

View file

@ -19,7 +19,7 @@ import { useState } from "react";
export const useLocalEcho = <T>( export const useLocalEcho = <T>(
currentFactory: () => T, currentFactory: () => T,
setterFn: (value: T) => Promise<unknown>, setterFn: (value: T) => Promise<unknown>,
errorFn: (error: Error) => void, errorFn: (error: unknown) => void,
): [value: T, handler: (value: T) => void] => { ): [value: T, handler: (value: T) => void] => {
const [value, setValue] = useState(currentFactory); const [value, setValue] = useState(currentFactory);
const handler = async (value: T): Promise<void> => { const handler = async (value: T): Promise<void> => {

View file

@ -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 <nativeLink>search components added</nativeLink>.": "%(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 <nativeLink>search components added</nativeLink>.", "%(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 <nativeLink>search components added</nativeLink>.": "%(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 <nativeLink>search components added</nativeLink>.",
"%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> for encrypted messages to appear in search results.": "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> for encrypted messages to appear in search results.", "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> for encrypted messages to appear in search results.": "%(brand)s can't securely cache encrypted messages locally while running in a web browser. Use <desktopLink>%(brand)s Desktop</desktopLink> for encrypted messages to appear in search results.",
"Message search initialisation failed": "Message search initialisation failed", "Message search initialisation failed": "Message search initialisation failed",
"Unknown error": "Unknown error",
"Hey you. You're the best!": "Hey you. You're the best!", "Hey you. You're the best!": "Hey you. You're the best!",
"Size must be a number": "Size must be a number", "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", "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", "Remove %(count)s messages|one": "Remove 1 message",
"Can't start voice message": "Can't start voice 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.", "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", "Unable to load commit detail: %(msg)s": "Unable to load commit detail: %(msg)s",
"Unavailable": "Unavailable", "Unavailable": "Unavailable",
"Changelog": "Changelog", "Changelog": "Changelog",

View file

@ -37,7 +37,7 @@ const INDEX_VERSION = 1;
*/ */
export class EventIndexPeg { export class EventIndexPeg {
public index: EventIndex | null = null; public index: EventIndex | null = null;
public error: Error | null = null; public error: unknown;
private _supportIsInstalled = false; private _supportIsInstalled = false;

View file

@ -174,7 +174,7 @@ async function getContexts(): Promise<Contexts> {
}; };
} }
export async function sendSentryReport(userText: string, issueUrl: string, error?: Error): Promise<void> { export async function sendSentryReport(userText: string, issueUrl: string, error?: unknown): Promise<void> {
const sentryConfig = SdkConfig.getObject("sentry"); const sentryConfig = SdkConfig.getObject("sentry");
if (!sentryConfig) return; if (!sentryConfig) return;

View file

@ -105,14 +105,13 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
* Reset on successful publish of location * Reset on successful publish of location
*/ */
public readonly beaconLocationPublishErrorCounts = new Map<BeaconIdentifier, number>(); public readonly beaconLocationPublishErrorCounts = new Map<BeaconIdentifier, number>();
public readonly beaconUpdateErrors = new Map<BeaconIdentifier, Error>(); public readonly beaconUpdateErrors = new Map<BeaconIdentifier, unknown>();
/** /**
* ids of live beacons * ids of live beacons
* ordered by creation time descending * ordered by creation time descending
*/ */
private liveBeaconIds: BeaconIdentifier[] = []; private liveBeaconIds: BeaconIdentifier[] = [];
private locationInterval?: number; private locationInterval?: number;
private geolocationError?: GeolocationError;
private clearPositionWatch?: ClearWatchCallback; private clearPositionWatch?: ClearWatchCallback;
/** /**
* Track when the last position was published * Track when the last position was published
@ -462,7 +461,11 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
try { try {
this.clearPositionWatch = watchPosition(this.onWatchedPosition, this.onGeolocationError); this.clearPositionWatch = watchPosition(this.onWatchedPosition, this.onGeolocationError);
} catch (error) { } 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 // don't set locationInterval if geolocation failed to setup
return; return;
} }
@ -485,7 +488,6 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
clearInterval(this.locationInterval); clearInterval(this.locationInterval);
this.locationInterval = undefined; this.locationInterval = undefined;
this.lastPublishedPositionTimestamp = undefined; this.lastPublishedPositionTimestamp = undefined;
this.geolocationError = undefined;
if (this.clearPositionWatch) { if (this.clearPositionWatch) {
this.clearPositionWatch(); this.clearPositionWatch();
@ -507,8 +509,7 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
}; };
private onGeolocationError = async (error: GeolocationError): Promise<void> => { private onGeolocationError = async (error: GeolocationError): Promise<void> => {
this.geolocationError = error; logger.error("Geolocation failed", error);
logger.error("Geolocation failed", this.geolocationError);
// other errors are considered non-fatal // other errors are considered non-fatal
// and self recovering // and self recovering
@ -531,7 +532,11 @@ export class OwnBeaconStore extends AsyncStoreWithClient<OwnBeaconStoreState> {
const position = await getCurrentPosition(); const position = await getCurrentPosition();
this.publishLocationToBeacons(mapGeolocationPositionToTimedGeo(position)); this.publishLocationToBeacons(mapGeolocationPositionToTimedGeo(position));
} catch (error) { } catch (error) {
this.onGeolocationError(error?.message); if (error instanceof Error) {
this.onGeolocationError(error.message as GeolocationError);
} else {
console.error("Unexpected error", error);
}
} }
}; };

View file

@ -497,7 +497,7 @@ export class RoomViewStore extends EventEmitter {
action: Action.ViewRoomError, action: Action.ViewRoomError,
room_id: null, room_id: null,
room_alias: payload.room_alias, room_alias: payload.room_alias,
err, err: err instanceof MatrixError ? err : undefined,
}); });
return; return;
} }

View file

@ -43,11 +43,9 @@ export default class AutoDiscoveryUtils {
* @param {string | Error} error The error to check * @param {string | Error} error The error to check
* @returns {boolean} True if the error is a liveliness error. * @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; if (!error) return false;
return !!LIVELINESS_DISCOVERY_ERRORS.find((e) => return !!LIVELINESS_DISCOVERY_ERRORS.find((e) => (error instanceof Error ? e === error.message : e === error));
typeof error === "string" ? e === error : e === error.message,
);
} }
/** /**
@ -58,7 +56,7 @@ export default class AutoDiscoveryUtils {
* implementation for known values. * implementation for known values.
* @returns {*} The state for the component, given the error. * @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) { if (!err) {
return { return {
serverIsAlive: true, serverIsAlive: true,
@ -93,7 +91,7 @@ export default class AutoDiscoveryUtils {
} }
let isFatalError = true; 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) { if (errorMessage === AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER) {
isFatalError = false; isFatalError = false;
title = _t("Cannot reach identity server"); title = _t("Cannot reach identity server");

View file

@ -40,7 +40,7 @@ export async function doesIdentityServerHaveTerms(matrixClient: MatrixClient, fu
terms = await matrixClient.getTerms(SERVICE_TYPES.IS, fullUrl); terms = await matrixClient.getTerms(SERVICE_TYPES.IS, fullUrl);
} catch (e) { } catch (e) {
logger.error(e); logger.error(e);
if (e.cors === "rejected" || (e instanceof HTTPError && e.httpStatus === 404)) { if (e instanceof HTTPError && e.httpStatus === 404) {
terms = null; terms = null;
} else { } else {
throw e; throw e;

View file

@ -41,8 +41,8 @@ const isGeolocationPositionError = (error: unknown): error is GeolocationPositio
/** /**
* Maps GeolocationPositionError to our GeolocationError enum * Maps GeolocationPositionError to our GeolocationError enum
*/ */
export const mapGeolocationError = (error: GeolocationPositionError | Error): GeolocationError => { export const mapGeolocationError = (error: GeolocationPositionError | Error | unknown): GeolocationError => {
logger.error("Geolocation failed", error?.message ?? error); logger.error("Geolocation failed", error);
if (isGeolocationPositionError(error)) { if (isGeolocationPositionError(error)) {
switch (error?.code) { switch (error?.code) {
@ -55,7 +55,7 @@ export const mapGeolocationError = (error: GeolocationPositionError | Error): Ge
default: default:
return GeolocationError.Default; return GeolocationError.Default;
} }
} else if (error.message === GeolocationError.Unavailable) { } else if (error instanceof Error && error.message === GeolocationError.Unavailable) {
return GeolocationError.Unavailable; return GeolocationError.Unavailable;
} else { } else {
return GeolocationError.Default; return GeolocationError.Default;

View file

@ -105,8 +105,10 @@ export async function leaveRoomBehaviour(
if (e instanceof MatrixError) { if (e instanceof MatrixError) {
const message = e.data.error || _t("Unexpected server error trying to leave the room"); 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 }); results[roomId] = Object.assign(new Error(message), { errcode: e.data.errcode, data: e.data });
} else if (e instanceof Error) {
results[roomId] = e;
} else { } 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 { } else {

View file

@ -41,7 +41,10 @@ export const useMap = ({ interactive, bodyId, onError }: UseMapProps): MapLibreM
try { try {
setMap(createMap(cli, !!interactive, bodyId, onError)); setMap(createMap(cli, !!interactive, bodyId, onError));
} catch (error) { } catch (error) {
onError?.(error); console.error("Error encountered in useMap", error);
if (error instanceof Error) {
onError?.(error);
}
} }
return () => { return () => {
if (map) { if (map) {

View file

@ -79,7 +79,7 @@ describe("<EventIndexPanel />", () => {
jest.spyOn(SettingsStore, "getValueAt").mockReturnValue(true); jest.spyOn(SettingsStore, "getValueAt").mockReturnValue(true);
// @ts-ignore private property // @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", () => { it("displays an error when no event index is found and enabling not in progress", () => {

View file

@ -1101,7 +1101,10 @@ describe("OwnBeaconStore", () => {
// still sharing // still sharing
expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled(); expect(mockClient.unstable_setLiveBeacon).not.toHaveBeenCalled();
expect(store.isMonitoringLiveLocation).toEqual(true); 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 () => { it("publishes last known position after 30s of inactivity", async () => {

View file

@ -13,8 +13,7 @@
"declaration": true, "declaration": true,
"jsx": "react", "jsx": "react",
"lib": ["es2020", "dom", "dom.iterable"], "lib": ["es2020", "dom", "dom.iterable"],
"strict": true, "strict": true
"useUnknownInCatchVariables": false
}, },
"include": [ "include": [
"./node_modules/matrix-js-sdk/src/@types/*.d.ts", "./node_modules/matrix-js-sdk/src/@types/*.d.ts",