diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index afec1f62f4..ef34746e39 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -51,7 +51,7 @@ import { isBulkUnverifiedDeviceReminderSnoozed } from "./utils/device/snoozeBulk const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000; export default class DeviceListener { - private dispatcherRef: string | null; + private dispatcherRef?: string; // device IDs for which the user has dismissed the verify toast ('Later') private dismissed = new Set(); // has the user dismissed any of the various nag toasts to setup encryption on this device? @@ -119,7 +119,7 @@ export default class DeviceListener { } if (this.dispatcherRef) { dis.unregister(this.dispatcherRef); - this.dispatcherRef = null; + this.dispatcherRef = undefined; } this.dismissed.clear(); this.dismissedThisDeviceToast = false; diff --git a/src/IdentityAuthClient.tsx b/src/IdentityAuthClient.tsx index 4df9959511..5ad918d0a3 100644 --- a/src/IdentityAuthClient.tsx +++ b/src/IdentityAuthClient.tsx @@ -34,8 +34,8 @@ import { abbreviateUrl } from "./utils/UrlUtils"; export class AbortedIdentityActionError extends Error {} export default class IdentityAuthClient { - private accessToken: string; - private tempClient: MatrixClient; + private accessToken: string | null = null; + private tempClient?: MatrixClient; private authEnabled = true; /** diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index 3aae805fae..52d91dc9d1 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -139,7 +139,7 @@ class MatrixClientPegClass implements IMatrixClientPeg { // the credentials used to init the current client object. // used if we tear it down & recreate it with a different store - private currentClientCreds: IMatrixClientCreds; + private currentClientCreds: IMatrixClientCreds | null = null; public get(): MatrixClient { return this.matrixClient; diff --git a/src/NodeAnimator.tsx b/src/NodeAnimator.tsx index f793b90a92..2a496fa4b2 100644 --- a/src/NodeAnimator.tsx +++ b/src/NodeAnimator.tsx @@ -42,7 +42,7 @@ interface IProps { */ export default class NodeAnimator extends React.Component { private nodes: Record = {}; - private children: { [key: string]: ReactElement }; + private children: { [key: string]: ReactElement } = {}; public static defaultProps: Partial = { startStyles: [], }; diff --git a/src/audio/VoiceRecording.ts b/src/audio/VoiceRecording.ts index f4d7905c33..033c5da475 100644 --- a/src/audio/VoiceRecording.ts +++ b/src/audio/VoiceRecording.ts @@ -66,14 +66,14 @@ export enum RecordingState { } export class VoiceRecording extends EventEmitter implements IDestroyable { - private recorder: Recorder; - private recorderContext: AudioContext; - private recorderSource: MediaStreamAudioSourceNode; - private recorderStream: MediaStream; - private recorderWorklet: AudioWorkletNode; - private recorderProcessor: ScriptProcessorNode; + private recorder?: Recorder; + private recorderContext?: AudioContext; + private recorderSource?: MediaStreamAudioSourceNode; + private recorderStream?: MediaStream; + private recorderWorklet?: AudioWorkletNode; + private recorderProcessor?: ScriptProcessorNode; private recording = false; - private observable: SimpleObservable; + private observable?: SimpleObservable; private targetMaxLength: number | null = TARGET_MAX_LENGTH; public amplitudes: number[] = []; // at each second mark, generated private liveWaveform = new FixedRollingArray(RECORDING_PLAYBACK_SAMPLES, 0); @@ -84,7 +84,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { } public get durationSeconds(): number { - if (!this.recorder) throw new Error("Duration not available without a recording"); + if (!this.recorder || !this.recorderContext) throw new Error("Duration not available without a recording"); return this.recorderContext.currentTime; } @@ -203,7 +203,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { } public get liveData(): SimpleObservable { - if (!this.recording) throw new Error("No observable when not recording"); + if (!this.recording || !this.observable) throw new Error("No observable when not recording"); return this.observable; } @@ -221,7 +221,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { private processAudioUpdate = (timeSeconds: number): void => { if (!this.recording) return; - this.observable.update({ + this.observable!.update({ waveform: this.liveWaveform.value.map((v) => clamp(v, 0, 1)), timeSeconds: timeSeconds, }); @@ -272,7 +272,7 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { } this.observable = new SimpleObservable(); await this.makeRecorder(); - await this.recorder.start(); + await this.recorder?.start(); this.recording = true; this.emit(RecordingState.Started); } @@ -284,8 +284,8 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { } // Disconnect the source early to start shutting down resources - await this.recorder.stop(); // stop first to flush the last frame - this.recorderSource.disconnect(); + await this.recorder!.stop(); // stop first to flush the last frame + this.recorderSource!.disconnect(); if (this.recorderWorklet) this.recorderWorklet.disconnect(); if (this.recorderProcessor) { this.recorderProcessor.disconnect(); @@ -294,14 +294,14 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { // close the context after the recorder so the recorder doesn't try to // connect anything to the context (this would generate a warning) - await this.recorderContext.close(); + await this.recorderContext!.close(); // Now stop all the media tracks so we can release them back to the user/OS - this.recorderStream.getTracks().forEach((t) => t.stop()); + this.recorderStream!.getTracks().forEach((t) => t.stop()); // Finally do our post-processing and clean up this.recording = false; - await this.recorder.close(); + await this.recorder!.close(); this.emit(RecordingState.Ended); }); } @@ -313,6 +313,6 @@ export class VoiceRecording extends EventEmitter implements IDestroyable { this.onDataAvailable = undefined; Singleflight.forgetAllFor(this); // noinspection JSIgnoredPromiseFromCall - not concerned about being called async here - this.observable.close(); + this.observable?.close(); } } diff --git a/src/autocomplete/AutocompleteProvider.tsx b/src/autocomplete/AutocompleteProvider.tsx index 0b1fe2dd9e..ace9f541e3 100644 --- a/src/autocomplete/AutocompleteProvider.tsx +++ b/src/autocomplete/AutocompleteProvider.tsx @@ -36,8 +36,8 @@ export interface IAutocompleteOptions { } export default abstract class AutocompleteProvider { - public commandRegex: RegExp; - public forcedCommandRegex: RegExp; + public commandRegex?: RegExp; + public forcedCommandRegex?: RegExp; protected renderingType: TimelineRenderingType = TimelineRenderingType.Room; diff --git a/src/autocomplete/QueryMatcher.ts b/src/autocomplete/QueryMatcher.ts index 031f0b0122..ba052d1901 100644 --- a/src/autocomplete/QueryMatcher.ts +++ b/src/autocomplete/QueryMatcher.ts @@ -47,7 +47,7 @@ interface IOptions { */ export default class QueryMatcher { private _options: IOptions; - private _items: Map; + private _items = new Map(); public constructor(objects: T[], options: IOptions = { keys: [] }) { this._options = options; diff --git a/src/autocomplete/UserProvider.tsx b/src/autocomplete/UserProvider.tsx index 0ba5f656d8..22bd307b47 100644 --- a/src/autocomplete/UserProvider.tsx +++ b/src/autocomplete/UserProvider.tsx @@ -44,7 +44,7 @@ const FORCED_USER_REGEX = /[^/,:; \t\n]\S*/g; export default class UserProvider extends AutocompleteProvider { public matcher: QueryMatcher; - public users: RoomMember[] | null; + public users?: RoomMember[]; public room: Room; public constructor(room: Room, renderingType?: TimelineRenderingType) { @@ -98,7 +98,7 @@ export default class UserProvider extends AutocompleteProvider { if (state.roomId !== this.room.roomId) return; // blow away the users cache - this.users = null; + this.users = undefined; }; public async getCompletions( diff --git a/src/components/views/auth/PasswordLogin.tsx b/src/components/views/auth/PasswordLogin.tsx index 727505551a..bbab2242f4 100644 --- a/src/components/views/auth/PasswordLogin.tsx +++ b/src/components/views/auth/PasswordLogin.tsx @@ -67,9 +67,9 @@ const enum LoginField { * The email/username/phone fields are fully-controlled, the password field is not. */ export default class PasswordLogin extends React.PureComponent { - private [LoginField.Email]: Field | null; - private [LoginField.Phone]: Field | null; - private [LoginField.MatrixId]: Field | null; + private [LoginField.Email]: Field | null = null; + private [LoginField.Phone]: Field | null = null; + private [LoginField.MatrixId]: Field | null = null; public static defaultProps = { onUsernameChanged: function () {}, diff --git a/src/components/views/auth/RegistrationForm.tsx b/src/components/views/auth/RegistrationForm.tsx index 17d540b4ed..1906365e6f 100644 --- a/src/components/views/auth/RegistrationForm.tsx +++ b/src/components/views/auth/RegistrationForm.tsx @@ -95,11 +95,11 @@ interface IState { * A pure UI component which displays a registration form. */ export default class RegistrationForm extends React.PureComponent { - private [RegistrationField.Email]: Field | null; - private [RegistrationField.Password]: Field | null; - private [RegistrationField.PasswordConfirm]: Field | null; - private [RegistrationField.Username]: Field | null; - private [RegistrationField.PhoneNumber]: Field | null; + private [RegistrationField.Email]: Field | null = null; + private [RegistrationField.Password]: Field | null = null; + private [RegistrationField.PasswordConfirm]: Field | null = null; + private [RegistrationField.Username]: Field | null = null; + private [RegistrationField.PhoneNumber]: Field | null = null; public static defaultProps = { onValidationChange: logger.error, diff --git a/src/components/views/avatars/DecoratedRoomAvatar.tsx b/src/components/views/avatars/DecoratedRoomAvatar.tsx index 953ffc2288..3756e7c41a 100644 --- a/src/components/views/avatars/DecoratedRoomAvatar.tsx +++ b/src/components/views/avatars/DecoratedRoomAvatar.tsx @@ -78,7 +78,7 @@ function tooltipText(variant: Icon): string | undefined { } export default class DecoratedRoomAvatar extends React.PureComponent { - private _dmUser: User | null; + private _dmUser: User | null = null; private isUnmounted = false; private isWatchingTimeline = false; diff --git a/src/components/views/rooms/Autocomplete.tsx b/src/components/views/rooms/Autocomplete.tsx index daf96fd508..b8f643eebb 100644 --- a/src/components/views/rooms/Autocomplete.tsx +++ b/src/components/views/rooms/Autocomplete.tsx @@ -50,9 +50,9 @@ interface IState { } export default class Autocomplete extends React.PureComponent { - public autocompleter: Autocompleter; - public queryRequested: string; - public debounceCompletionsRequest: number; + public autocompleter?: Autocompleter; + public queryRequested?: string; + public debounceCompletionsRequest?: number; private containerRef = createRef(); public static contextType = RoomContext; @@ -86,7 +86,7 @@ export default class Autocomplete extends React.PureComponent { private applyNewProps(oldQuery?: string, oldRoom?: Room): void { if (oldRoom && this.props.room.roomId !== oldRoom.roomId) { - this.autocompleter.destroy(); + this.autocompleter?.destroy(); this.autocompleter = new Autocompleter(this.props.room); } @@ -99,7 +99,7 @@ export default class Autocomplete extends React.PureComponent { } public componentWillUnmount(): void { - this.autocompleter.destroy(); + this.autocompleter?.destroy(); } private complete(query: string, selection: ISelectionRange): Promise { diff --git a/src/components/views/rooms/BasicMessageComposer.tsx b/src/components/views/rooms/BasicMessageComposer.tsx index fa45e56d19..6a66d52eb2 100644 --- a/src/components/views/rooms/BasicMessageComposer.tsx +++ b/src/components/views/rooms/BasicMessageComposer.tsx @@ -132,7 +132,7 @@ export default class BasicMessageEditor extends React.Component private _isCaretAtEnd: boolean; private lastCaret: DocumentOffset; - private lastSelection: ReturnType | null; + private lastSelection: ReturnType | null = null; private readonly useMarkdownHandle: string; private readonly emoticonSettingHandle: string; diff --git a/src/components/views/rooms/Stickerpicker.tsx b/src/components/views/rooms/Stickerpicker.tsx index 4a3e809185..dc6c4fd34d 100644 --- a/src/components/views/rooms/Stickerpicker.tsx +++ b/src/components/views/rooms/Stickerpicker.tsx @@ -64,9 +64,9 @@ export default class Stickerpicker extends React.PureComponent { public static currentWidget?: IWidgetEvent; - private dispatcherRef: string; + private dispatcherRef?: string; - private prevSentVisibility: boolean; + private prevSentVisibility?: boolean; private popoverWidth = 300; private popoverHeight = 300; diff --git a/src/components/views/settings/ThemeChoicePanel.tsx b/src/components/views/settings/ThemeChoicePanel.tsx index 32c411cd77..909e020a7c 100644 --- a/src/components/views/settings/ThemeChoicePanel.tsx +++ b/src/components/views/settings/ThemeChoicePanel.tsx @@ -49,7 +49,7 @@ interface IState extends IThemeState { } export default class ThemeChoicePanel extends React.Component { - private themeTimer: number; + private themeTimer?: number; public constructor(props: IProps) { super(props); diff --git a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx index 9fe4528f5a..76f243e6ab 100644 --- a/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/SecurityUserSettingsTab.tsx @@ -87,7 +87,7 @@ interface IState { } export default class SecurityUserSettingsTab extends React.Component { - private dispatcherRef: string; + private dispatcherRef?: string; public constructor(props: IProps) { super(props); @@ -124,7 +124,7 @@ export default class SecurityUserSettingsTab extends React.Component { - private intervalHandle: number; + private intervalHandle?: number; public constructor(props: IProps) { super(props); diff --git a/src/components/views/voip/LegacyCallView.tsx b/src/components/views/voip/LegacyCallView.tsx index 5978acb316..aa0e24e735 100644 --- a/src/components/views/voip/LegacyCallView.tsx +++ b/src/components/views/voip/LegacyCallView.tsx @@ -100,7 +100,7 @@ function exitFullscreen(): void { } export default class LegacyCallView extends React.Component { - private dispatcherRef: string; + private dispatcherRef?: string; private contentWrapperRef = createRef(); private buttonsRef = createRef(); @@ -137,7 +137,7 @@ export default class LegacyCallView extends React.Component { document.removeEventListener("keydown", this.onNativeKeyDown); this.updateCallListeners(this.props.call, null); - dis.unregister(this.dispatcherRef); + if (this.dispatcherRef) dis.unregister(this.dispatcherRef); } public static getDerivedStateFromProps(props: IProps): Partial { diff --git a/src/components/views/voip/VideoFeed.tsx b/src/components/views/voip/VideoFeed.tsx index c02154936f..87c0eeb7f0 100644 --- a/src/components/views/voip/VideoFeed.tsx +++ b/src/components/views/voip/VideoFeed.tsx @@ -52,7 +52,7 @@ interface IState { } export default class VideoFeed extends React.PureComponent { - private element: HTMLVideoElement; + private element?: HTMLVideoElement; public constructor(props: IProps) { super(props); diff --git a/src/editor/autocomplete.ts b/src/editor/autocomplete.ts index 8d95ae523b..3aed32d91f 100644 --- a/src/editor/autocomplete.ts +++ b/src/editor/autocomplete.ts @@ -32,7 +32,7 @@ export type GetAutocompleterComponent = () => Autocomplete | null; export type UpdateQuery = (test: string) => Promise; export default class AutocompleteWrapperModel { - private partIndex: number; + private partIndex?: number; public constructor( private updateCallback: UpdateCallback, diff --git a/src/integrations/IntegrationManagers.ts b/src/integrations/IntegrationManagers.ts index da1bb62ba9..95d57a33bb 100644 --- a/src/integrations/IntegrationManagers.ts +++ b/src/integrations/IntegrationManagers.ts @@ -39,8 +39,8 @@ export class IntegrationManagers { private static instance?: IntegrationManagers; private managers: IntegrationManagerInstance[] = []; - private client: MatrixClient; - private primaryManager: IntegrationManagerInstance | null; + private client?: MatrixClient; + private primaryManager: IntegrationManagerInstance | null = null; public static sharedInstance(): IntegrationManagers { if (!IntegrationManagers.instance) { diff --git a/src/models/LocalRoom.ts b/src/models/LocalRoom.ts index c0bb3a75de..8c8d812d66 100644 --- a/src/models/LocalRoom.ts +++ b/src/models/LocalRoom.ts @@ -35,7 +35,7 @@ export class LocalRoom extends Room { /** Whether the actual room should be encrypted. */ public encrypted = false; /** If the actual room has been created, this holds its ID. */ - public actualRoomId: string; + public actualRoomId?: string; /** DM chat partner */ public targets: Member[] = []; /** Callbacks that should be invoked after the actual room has been created. */ diff --git a/src/models/RoomUpload.ts b/src/models/RoomUpload.ts index c86151d13e..feda356a29 100644 --- a/src/models/RoomUpload.ts +++ b/src/models/RoomUpload.ts @@ -20,7 +20,7 @@ import { IEncryptedFile } from "../customisations/models/IMediaEventContent"; export class RoomUpload { public readonly abortController = new AbortController(); - public promise: Promise<{ url?: string; file?: IEncryptedFile }>; + public promise?: Promise<{ url?: string; file?: IEncryptedFile }>; private uploaded = 0; public constructor( diff --git a/src/stores/ActiveWidgetStore.ts b/src/stores/ActiveWidgetStore.ts index c09586b0de..42e8d739ac 100644 --- a/src/stores/ActiveWidgetStore.ts +++ b/src/stores/ActiveWidgetStore.ts @@ -39,8 +39,8 @@ export enum ActiveWidgetStoreEvent { */ export default class ActiveWidgetStore extends EventEmitter { private static internalInstance: ActiveWidgetStore; - private persistentWidgetId: string | null; - private persistentRoomId: string | null; + private persistentWidgetId: string | null = null; + private persistentRoomId: string | null = null; private dockedWidgetsByUid = new Map(); public static get instance(): ActiveWidgetStore { diff --git a/src/stores/OwnProfileStore.ts b/src/stores/OwnProfileStore.ts index a356338053..db8960307b 100644 --- a/src/stores/OwnProfileStore.ts +++ b/src/stores/OwnProfileStore.ts @@ -43,7 +43,7 @@ export class OwnProfileStore extends AsyncStoreWithClient { return instance; })(); - private monitoredUser: User | null; + private monitoredUser: User | null = null; private constructor() { // seed from localstorage because otherwise we won't get these values until a whole network diff --git a/src/stores/local-echo/GenericEchoChamber.ts b/src/stores/local-echo/GenericEchoChamber.ts index e1f418fbd9..2470c583da 100644 --- a/src/stores/local-echo/GenericEchoChamber.ts +++ b/src/stores/local-echo/GenericEchoChamber.ts @@ -28,7 +28,7 @@ export const PROPERTY_UPDATED = "property_updated"; export abstract class GenericEchoChamber extends EventEmitter { private cache = new Map(); - protected matrixClient: MatrixClient | null; + protected matrixClient: MatrixClient | null = null; protected constructor(public readonly context: C, private lookupFn: (key: K) => V) { super(); diff --git a/src/utils/LazyValue.ts b/src/utils/LazyValue.ts index b9de7e5ad7..2497a771d7 100644 --- a/src/utils/LazyValue.ts +++ b/src/utils/LazyValue.ts @@ -18,8 +18,8 @@ limitations under the License. * Utility class for lazily getting a variable. */ export class LazyValue { - private val: T; - private prom: Promise; + private val?: T; + private prom?: Promise; private done = false; public constructor(private getFn: () => Promise) {} @@ -36,7 +36,7 @@ export class LazyValue { * Gets the value without invoking a get. May be undefined until the * value is fetched properly. */ - public get cachedValue(): T { + public get cachedValue(): T | undefined { return this.val; } diff --git a/src/utils/ValidatedServerConfig.ts b/src/utils/ValidatedServerConfig.ts index 7d2325d510..bac271eef6 100644 --- a/src/utils/ValidatedServerConfig.ts +++ b/src/utils/ValidatedServerConfig.ts @@ -14,16 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -export class ValidatedServerConfig { - public hsUrl: string; - public hsName: string; - public hsNameIsDifferent: boolean; +export interface ValidatedServerConfig { + hsUrl: string; + hsName: string; + hsNameIsDifferent: boolean; - public isUrl: string; + isUrl: string; - public isDefault: boolean; + isDefault: boolean; // when the server config is based on static URLs the hsName is not resolvable and things may wish to use hsUrl - public isNameResolvable: boolean; + isNameResolvable: boolean; - public warning: string | Error; + warning: string | Error; } diff --git a/src/widgets/Jitsi.ts b/src/widgets/Jitsi.ts index 6dca3b3de7..af250e91bf 100644 --- a/src/widgets/Jitsi.ts +++ b/src/widgets/Jitsi.ts @@ -31,7 +31,7 @@ export interface JitsiWidgetData { export class Jitsi { private static instance: Jitsi; - private domain: string; + private domain?: string; public get preferredDomain(): string { return this.domain || "meet.element.io"; diff --git a/test/components/structures/auth/ForgotPassword-test.tsx b/test/components/structures/auth/ForgotPassword-test.tsx index 7feef7d6a1..d6bc962082 100644 --- a/test/components/structures/auth/ForgotPassword-test.tsx +++ b/test/components/structures/auth/ForgotPassword-test.tsx @@ -76,8 +76,7 @@ describe("", () => { client = stubClient(); mocked(createClient).mockReturnValue(client); - serverConfig = new ValidatedServerConfig(); - serverConfig.hsName = "example.com"; + serverConfig = { hsName: "example.com" } as ValidatedServerConfig; onComplete = jest.fn(); onLoginClick = jest.fn();