Improve quality of Typescript types (#10742)

pull/28788/head^2
Michael Telatynski 2023-05-05 09:11:14 +01:00 committed by GitHub
parent 542bf68c63
commit a4f0b80692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 74 additions and 75 deletions

View File

@ -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<string>();
// 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;

View File

@ -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;
/**

View File

@ -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;

View File

@ -42,7 +42,7 @@ interface IProps {
*/
export default class NodeAnimator extends React.Component<IProps> {
private nodes: Record<string, ReactInstance> = {};
private children: { [key: string]: ReactElement };
private children: { [key: string]: ReactElement } = {};
public static defaultProps: Partial<IProps> = {
startStyles: [],
};

View File

@ -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<IRecordingUpdate>;
private observable?: SimpleObservable<IRecordingUpdate>;
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<IRecordingUpdate> {
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<IRecordingUpdate>();
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();
}
}

View File

@ -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;

View File

@ -47,7 +47,7 @@ interface IOptions<T extends {}> {
*/
export default class QueryMatcher<T extends {}> {
private _options: IOptions<T>;
private _items: Map<string, { object: T; keyWeight: number }[]>;
private _items = new Map<string, { object: T; keyWeight: number }[]>();
public constructor(objects: T[], options: IOptions<T> = { keys: [] }) {
this._options = options;

View File

@ -44,7 +44,7 @@ const FORCED_USER_REGEX = /[^/,:; \t\n]\S*/g;
export default class UserProvider extends AutocompleteProvider {
public matcher: QueryMatcher<RoomMember>;
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(

View File

@ -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<IProps, IState> {
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 () {},

View File

@ -95,11 +95,11 @@ interface IState {
* A pure UI component which displays a registration form.
*/
export default class RegistrationForm extends React.PureComponent<IProps, IState> {
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,

View File

@ -78,7 +78,7 @@ function tooltipText(variant: Icon): string | undefined {
}
export default class DecoratedRoomAvatar extends React.PureComponent<IProps, IState> {
private _dmUser: User | null;
private _dmUser: User | null = null;
private isUnmounted = false;
private isWatchingTimeline = false;

View File

@ -50,9 +50,9 @@ interface IState {
}
export default class Autocomplete extends React.PureComponent<IProps, IState> {
public autocompleter: Autocompleter;
public queryRequested: string;
public debounceCompletionsRequest: number;
public autocompleter?: Autocompleter;
public queryRequested?: string;
public debounceCompletionsRequest?: number;
private containerRef = createRef<HTMLDivElement>();
public static contextType = RoomContext;
@ -86,7 +86,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
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<IProps, IState> {
}
public componentWillUnmount(): void {
this.autocompleter.destroy();
this.autocompleter?.destroy();
}
private complete(query: string, selection: ISelectionRange): Promise<void> {

View File

@ -132,7 +132,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
private _isCaretAtEnd: boolean;
private lastCaret: DocumentOffset;
private lastSelection: ReturnType<typeof cloneSelection> | null;
private lastSelection: ReturnType<typeof cloneSelection> | null = null;
private readonly useMarkdownHandle: string;
private readonly emoticonSettingHandle: string;

View File

@ -64,9 +64,9 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
public static currentWidget?: IWidgetEvent;
private dispatcherRef: string;
private dispatcherRef?: string;
private prevSentVisibility: boolean;
private prevSentVisibility?: boolean;
private popoverWidth = 300;
private popoverHeight = 300;

View File

@ -49,7 +49,7 @@ interface IState extends IThemeState {
}
export default class ThemeChoicePanel extends React.Component<IProps, IState> {
private themeTimer: number;
private themeTimer?: number;
public constructor(props: IProps) {
super(props);

View File

@ -87,7 +87,7 @@ interface IState {
}
export default class SecurityUserSettingsTab extends React.Component<IProps, IState> {
private dispatcherRef: string;
private dispatcherRef?: string;
public constructor(props: IProps) {
super(props);
@ -124,7 +124,7 @@ export default class SecurityUserSettingsTab extends React.Component<IProps, ISt
}
public componentWillUnmount(): void {
dis.unregister(this.dispatcherRef);
if (this.dispatcherRef) dis.unregister(this.dispatcherRef);
MatrixClientPeg.get().removeListener(RoomEvent.MyMembership, this.onMyMembership);
}

View File

@ -47,7 +47,7 @@ interface IState {
}
export default class VerificationRequestToast extends React.PureComponent<IProps, IState> {
private intervalHandle: number;
private intervalHandle?: number;
public constructor(props: IProps) {
super(props);

View File

@ -100,7 +100,7 @@ function exitFullscreen(): void {
}
export default class LegacyCallView extends React.Component<IProps, IState> {
private dispatcherRef: string;
private dispatcherRef?: string;
private contentWrapperRef = createRef<HTMLDivElement>();
private buttonsRef = createRef<LegacyCallViewButtons>();
@ -137,7 +137,7 @@ export default class LegacyCallView extends React.Component<IProps, IState> {
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<IState> {

View File

@ -52,7 +52,7 @@ interface IState {
}
export default class VideoFeed extends React.PureComponent<IProps, IState> {
private element: HTMLVideoElement;
private element?: HTMLVideoElement;
public constructor(props: IProps) {
super(props);

View File

@ -32,7 +32,7 @@ export type GetAutocompleterComponent = () => Autocomplete | null;
export type UpdateQuery = (test: string) => Promise<void>;
export default class AutocompleteWrapperModel {
private partIndex: number;
private partIndex?: number;
public constructor(
private updateCallback: UpdateCallback,

View File

@ -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) {

View File

@ -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. */

View File

@ -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(

View File

@ -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<string, number>();
public static get instance(): ActiveWidgetStore {

View File

@ -43,7 +43,7 @@ export class OwnProfileStore extends AsyncStoreWithClient<IState> {
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

View File

@ -28,7 +28,7 @@ export const PROPERTY_UPDATED = "property_updated";
export abstract class GenericEchoChamber<C extends EchoContext, K, V> extends EventEmitter {
private cache = new Map<K, { txn: EchoTransaction; val: V }>();
protected matrixClient: MatrixClient | null;
protected matrixClient: MatrixClient | null = null;
protected constructor(public readonly context: C, private lookupFn: (key: K) => V) {
super();

View File

@ -18,8 +18,8 @@ limitations under the License.
* Utility class for lazily getting a variable.
*/
export class LazyValue<T> {
private val: T;
private prom: Promise<T>;
private val?: T;
private prom?: Promise<T>;
private done = false;
public constructor(private getFn: () => Promise<T>) {}
@ -36,7 +36,7 @@ export class LazyValue<T> {
* 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;
}

View File

@ -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;
}

View File

@ -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";

View File

@ -76,8 +76,7 @@ describe("<ForgotPassword>", () => {
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();