Просмотр исходного кода

Update to eslint-plugin-matrix-org 0.8 (#23825)

* Update to eslint-plugin-matrix-org 0.8

* Exclude some eslint rules for specs

* Fix eslint tests path
Michael Weimann 3 лет назад
Родитель
Сommit
cbf5c43ae7

+ 9 - 0
.eslintrc.js

@@ -72,5 +72,14 @@ module.exports = {
                 }],
             }],
         },
+    }, {
+        files: [
+            "test/**/*.{ts,tsx}",
+        ],
+        rules: {
+            // We don't need super strict typing in test utilities
+            "@typescript-eslint/explicit-function-return-type": "off",
+            "@typescript-eslint/explicit-member-accessibility": "off",
+        },
     }],
 };

+ 3 - 3
module_system/installer.ts

@@ -123,12 +123,12 @@ function readCurrentPackageDetails(): RawDependencies {
     };
 }
 
-function writePackageDetails(deps: RawDependencies) {
+function writePackageDetails(deps: RawDependencies): void {
     fs.writeFileSync("./yarn.lock", deps.lockfile, "utf-8");
     fs.writeFileSync("./package.json", deps.packageJson, "utf-8");
 }
 
-function callYarnAdd(dep: string) {
+function callYarnAdd(dep: string): void {
     // Add the module to the optional dependencies section just in case something
     // goes wrong in restoring the original package details.
     childProcess.execSync(`yarn add -O ${dep}`, {
@@ -186,6 +186,6 @@ function isModuleVersionCompatible(ourApiVersion: string, moduleApiVersion: stri
     return semver.satisfies(ourApiVersion, moduleApiVersion);
 }
 
-function writeModulesTs(content: string) {
+function writeModulesTs(content: string): void {
     fs.writeFileSync("./src/modules.ts", content, "utf-8");
 }

+ 1 - 1
package.json

@@ -115,7 +115,7 @@
     "eslint-config-google": "^0.14.0",
     "eslint-plugin-deprecate": "^0.7.0",
     "eslint-plugin-import": "^2.25.4",
-    "eslint-plugin-matrix-org": "^0.7.0",
+    "eslint-plugin-matrix-org": "^0.8.0",
     "eslint-plugin-react": "^7.28.0",
     "eslint-plugin-react-hooks": "^4.3.0",
     "eslint-plugin-unicorn": "^44.0.2",

+ 2 - 2
src/components/views/auth/VectorAuthFooter.tsx

@@ -15,11 +15,11 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-import React from 'react';
+import React, { ReactElement } from 'react';
 import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
 import { _t } from 'matrix-react-sdk/src/languageHandler';
 
-const VectorAuthFooter = () => {
+const VectorAuthFooter = (): ReactElement => {
     const brandingConfig = SdkConfig.getObject("branding");
     const links = brandingConfig?.get("auth_footer_links") ?? [
         { "text": "Blog", "url": "https://element.io/blog" },

+ 1 - 1
src/components/views/auth/VectorAuthHeaderLogo.tsx

@@ -19,7 +19,7 @@ import * as React from 'react';
 import SdkConfig from 'matrix-react-sdk/src/SdkConfig';
 
 export default class VectorAuthHeaderLogo extends React.PureComponent {
-    public render() {
+    public render(): React.ReactElement {
         const brandingConfig = SdkConfig.getObject("branding");
         const logoUrl = brandingConfig?.get("auth_header_logo_url") ?? "themes/element/img/logos/element-logo.svg";
 

+ 2 - 2
src/components/views/auth/VectorAuthPage.tsx

@@ -23,7 +23,7 @@ export default class VectorAuthPage extends React.PureComponent {
     private static welcomeBackgroundUrl;
 
     // cache the url as a static to prevent it changing without refreshing
-    private static getWelcomeBackgroundUrl() {
+    private static getWelcomeBackgroundUrl(): string {
         if (VectorAuthPage.welcomeBackgroundUrl) return VectorAuthPage.welcomeBackgroundUrl;
 
         const brandingConfig = SdkConfig.getObject("branding");
@@ -42,7 +42,7 @@ export default class VectorAuthPage extends React.PureComponent {
         return VectorAuthPage.welcomeBackgroundUrl;
     }
 
-    public render() {
+    public render(): React.ReactElement {
         const pageStyle = {
             background: `center/cover fixed url(${VectorAuthPage.getWelcomeBackgroundUrl()})`,
         };

+ 3 - 3
src/favicon.ts

@@ -56,7 +56,7 @@ export default class Favicon {
     // callback to run once isReady is asserted, allows for a badge to be queued for when it can be shown
     private readyCb?: () => void;
 
-    constructor(params: Partial<IParams> = {}) {
+    public constructor(params: Partial<IParams> = {}) {
         this.params = { ...defaults, ...params };
 
         this.icons = Favicon.getIcons();
@@ -68,7 +68,7 @@ export default class Favicon {
         const lastIcon = this.icons[this.icons.length - 1];
         if (lastIcon.hasAttribute("href")) {
             this.baseImage.setAttribute("crossOrigin", "anonymous");
-            this.baseImage.onload = () => {
+            this.baseImage.onload = (): void => {
                 // get height and width of the favicon
                 this.canvas.height = (this.baseImage.height > 0) ? this.baseImage.height : 32;
                 this.canvas.width = (this.baseImage.width > 0) ? this.baseImage.width : 32;
@@ -217,7 +217,7 @@ export default class Favicon {
 
     public badge(content: number | string, opts?: Partial<IParams>): void {
         if (!this.isReady) {
-            this.readyCb = () => {
+            this.readyCb = (): void => {
                 this.badge(content, opts);
             };
             return;

+ 4 - 4
src/vector/app.tsx

@@ -21,7 +21,7 @@ limitations under the License.
 // To ensure we load the browser-matrix version first
 import "matrix-js-sdk/src/browser-index";
 
-import React from 'react';
+import React, { ReactElement } from 'react';
 import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg';
 import { _td, newTranslatableError } from 'matrix-react-sdk/src/languageHandler';
 import AutoDiscoveryUtils from 'matrix-react-sdk/src/utils/AutoDiscoveryUtils';
@@ -55,7 +55,7 @@ window.matrixLogger = logger;
 // If we're in electron, we should never pass through a file:// URL otherwise
 // the identity server will try to 302 the browser to it, which breaks horribly.
 // so in that instance, hardcode to use app.element.io for now instead.
-function makeRegistrationUrl(params: object) {
+function makeRegistrationUrl(params: object): string {
     let url;
     if (window.location.protocol === "vector:") {
         url = 'https://app.element.io/#/register';
@@ -81,7 +81,7 @@ function makeRegistrationUrl(params: object) {
     return url;
 }
 
-function onTokenLoginCompleted() {
+function onTokenLoginCompleted(): void {
     // if we did a token login, we're now left with the token, hs and is
     // url as query params in the url; a little nasty but let's redirect to
     // clear them.
@@ -93,7 +93,7 @@ function onTokenLoginCompleted() {
     window.history.replaceState(null, "", url.href);
 }
 
-export async function loadApp(fragParams: {}) {
+export async function loadApp(fragParams: {}): Promise<ReactElement> {
     initRouting();
     const platform = PlatformPeg.get();
 

+ 3 - 3
src/vector/index.ts

@@ -40,7 +40,7 @@ require('katex/dist/katex.css');
 require('./devcss');
 require('./localstorage-fix');
 
-async function settled(...promises: Array<Promise<any>>) {
+async function settled(...promises: Array<Promise<any>>): Promise<void> {
     for (const prom of promises) {
         try {
             await prom;
@@ -50,7 +50,7 @@ async function settled(...promises: Array<Promise<any>>) {
     }
 }
 
-function checkBrowserFeatures() {
+function checkBrowserFeatures(): boolean {
     if (!window.Modernizr) {
         logger.error("Cannot check features - Modernizr global is missing.");
         return false;
@@ -102,7 +102,7 @@ const supportedBrowser = checkBrowserFeatures();
 // We start loading stuff but don't block on it until as late as possible to allow
 // the browser to use as much parallelism as it can.
 // Load parallelism is based on research in https://github.com/vector-im/element-web/issues/12253
-async function start() {
+async function start(): Promise<void> {
     // load init.ts async so that its code is not executed immediately and we can catch any exceptions
     const {
         rageshakePromise,

+ 9 - 9
src/vector/init.tsx

@@ -41,7 +41,7 @@ import { INSTALLED_MODULES } from "../modules";
 
 export const rageshakePromise = initRageshake();
 
-export function preparePlatform() {
+export function preparePlatform(): void {
     if (window.electron) {
         logger.log("Using Electron platform");
         PlatformPeg.set(new ElectronPlatform());
@@ -54,7 +54,7 @@ export function preparePlatform() {
     }
 }
 
-export function setupLogStorage() {
+export function setupLogStorage(): Promise<void> {
     if (SdkConfig.get().bug_report_endpoint_url) {
         return initRageshakeStore();
     }
@@ -62,7 +62,7 @@ export function setupLogStorage() {
     return Promise.resolve();
 }
 
-export async function loadConfig() {
+export async function loadConfig(): Promise<void> {
     // XXX: We call this twice, once here and once in MatrixChat as a prop. We call it here to ensure
     // granular settings are loaded correctly and to avoid duplicating the override logic for the theme.
     //
@@ -112,7 +112,7 @@ export function loadOlm(): Promise<void> {
     });
 }
 
-export async function loadLanguage() {
+export async function loadLanguage(): Promise<void> {
     const prefLang = SettingsStore.getValue("language", null, /*excludeDefault=*/true);
     let langs = [];
 
@@ -131,11 +131,11 @@ export async function loadLanguage() {
     }
 }
 
-export async function loadTheme() {
+export async function loadTheme(): Promise<void> {
     setTheme();
 }
 
-export async function loadApp(fragParams: {}) {
+export async function loadApp(fragParams: {}): Promise<void> {
     // load app.js async so that its code is not executed immediately and we can catch any exceptions
     const module = await import(
         /* webpackChunkName: "element-web-app" */
@@ -145,7 +145,7 @@ export async function loadApp(fragParams: {}) {
         document.getElementById('matrixchat'));
 }
 
-export async function showError(title: string, messages?: string[]) {
+export async function showError(title: string, messages?: string[]): Promise<void> {
     const ErrorView = (await import(
         /* webpackChunkName: "error-view" */
         "../async-components/structures/ErrorView")).default;
@@ -153,7 +153,7 @@ export async function showError(title: string, messages?: string[]) {
         document.getElementById('matrixchat'));
 }
 
-export async function showIncompatibleBrowser(onAccept) {
+export async function showIncompatibleBrowser(onAccept): Promise<void> {
     const CompatibilityView = (await import(
         /* webpackChunkName: "compatibility-view" */
         "../async-components/structures/CompatibilityView")).default;
@@ -161,7 +161,7 @@ export async function showIncompatibleBrowser(onAccept) {
         document.getElementById('matrixchat'));
 }
 
-export async function loadModules() {
+export async function loadModules(): Promise<void> {
     for (const InstalledModule of INSTALLED_MODULES) {
         // eslint-disable-next-line @typescript-eslint/ban-ts-comment
         // @ts-ignore - we know the constructor exists even if TypeScript can't be convinced of that

+ 17 - 17
src/vector/jitsi/index.ts

@@ -61,7 +61,7 @@ let widgetApi: WidgetApi;
 let meetApi: any; // JitsiMeetExternalAPI
 let skipOurWelcomeScreen = false;
 
-const setupCompleted = (async () => {
+const setupCompleted = (async (): Promise<string | void> => {
     try {
         // Queue a config.json lookup asap, so we can use it later on. We want this to be concurrent with
         // other setup work and therefore do not block.
@@ -223,11 +223,11 @@ const setupCompleted = (async () => {
     }
 })();
 
-function enableJoinButton() {
-    document.getElementById("joinButton").onclick = () => joinConference();
+function enableJoinButton(): void {
+    document.getElementById("joinButton").onclick = (): void => joinConference();
 }
 
-function switchVisibleContainers() {
+function switchVisibleContainers(): void {
     inConference = !inConference;
 
     // Our welcome screen is managed by other code, so just don't switch to it ever
@@ -237,14 +237,14 @@ function switchVisibleContainers() {
     }
 }
 
-function toggleConferenceVisibility(inConference: boolean) {
+function toggleConferenceVisibility(inConference: boolean): void {
     document.getElementById("jitsiContainer").style.visibility = inConference ? 'unset' : 'hidden';
     // Video rooms have a separate UI for joining, so they should never show our join button
     document.getElementById("joinButtonContainer").style.visibility =
         (inConference || isVideoChannel) ? 'hidden' : 'unset';
 }
 
-function skipToJitsiSplashScreen() {
+function skipToJitsiSplashScreen(): void {
     // really just a function alias for self-documenting code
     joinConference();
 }
@@ -254,7 +254,7 @@ function skipToJitsiSplashScreen() {
  *
  * See https://github.com/matrix-org/prosody-mod-auth-matrix-user-verification
  */
-function createJWTToken() {
+function createJWTToken(): string {
     // Header
     const header = { alg: 'HS256', typ: 'JWT' };
     // Payload
@@ -289,7 +289,7 @@ function createJWTToken() {
     );
 }
 
-async function notifyHangup(errorMessage?: string) {
+async function notifyHangup(errorMessage?: string): Promise<void> {
     if (widgetApi) {
         // We send the hangup event before setAlwaysOnScreen, because the latter
         // can cause the receiving side to instantly stop listening.
@@ -301,7 +301,7 @@ async function notifyHangup(errorMessage?: string) {
     }
 }
 
-function closeConference() {
+function closeConference(): void {
     switchVisibleContainers();
     document.getElementById("jitsiContainer").innerHTML = "";
 
@@ -315,7 +315,7 @@ function closeConference() {
 // audio input it can find, while an input of null instructs it to start muted,
 // and a non-nullish input specifies the label of a specific device to use.
 // Same for video inputs.
-function joinConference(audioInput?: string | null, videoInput?: string | null) {
+function joinConference(audioInput?: string | null, videoInput?: string | null): void {
     let jwt;
     if (jitsiAuth === JITSI_OPENIDTOKEN_JWT_AUTH) {
         if (!openIdToken?.access_token) { // eslint-disable-line camelcase
@@ -408,7 +408,7 @@ function joinConference(audioInput?: string | null, videoInput?: string | null)
     meetApi.on("log", onLog);
 }
 
-const onVideoConferenceJoined = () => {
+const onVideoConferenceJoined = (): void => {
     // Although we set our displayName with the userInfo option above, that
     // option has a bug where it causes the name to be the HTML encoding of
     // what was actually intended. So, we use the displayName command to at
@@ -432,12 +432,12 @@ const onVideoConferenceJoined = () => {
     if (isVideoChannel) meetApi.executeCommand("setTileView", true);
 };
 
-const onVideoConferenceLeft = () => {
+const onVideoConferenceLeft = (): void => {
     notifyHangup();
     meetApi = null;
 };
 
-const onErrorOccurred = ({ error }) => {
+const onErrorOccurred = ({ error }): void => {
     if (error.isFatal) {
         // We got disconnected. Since Jitsi Meet might send us back to the
         // prejoin screen, we're forced to act as if we hung up entirely.
@@ -447,12 +447,12 @@ const onErrorOccurred = ({ error }) => {
     }
 };
 
-const onAudioMuteStatusChanged = ({ muted }) => {
+const onAudioMuteStatusChanged = ({ muted }): void => {
     const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio;
     widgetApi?.transport.send(action, {});
 };
 
-const onVideoMuteStatusChanged = ({ muted }) => {
+const onVideoMuteStatusChanged = ({ muted }): void => {
     if (muted) {
         // Jitsi Meet always sends a "video muted" event directly before
         // hanging up, which we need to ignore by padding the timeout here,
@@ -466,11 +466,11 @@ const onVideoMuteStatusChanged = ({ muted }) => {
     }
 };
 
-const updateParticipants = () => {
+const updateParticipants = (): void => {
     widgetApi?.transport.send(ElementWidgetActions.CallParticipants, {
         participants: meetApi.getParticipantsInfo(),
     });
 };
 
-const onLog = ({ logLevel, args }) =>
+const onLog = ({ logLevel, args }): void =>
     (parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args);

+ 12 - 12
src/vector/platform/ElectronPlatform.tsx

@@ -92,7 +92,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
     // this is the opaque token we pass to the HS which when we get it in our callback we can resolve to a profile
     private readonly ssoID: string = randomString(32);
 
-    constructor() {
+    public constructor() {
         super();
 
         dis.register(onAction);
@@ -124,12 +124,12 @@ export default class ElectronPlatform extends VectorBasePlatform {
         window.electron.on('userDownloadCompleted', (ev, { id, name }) => {
             const key = `DOWNLOAD_TOAST_${id}`;
 
-            const onAccept = () => {
+            const onAccept = (): void => {
                 window.electron.send('userDownloadAction', { id, open: true });
                 ToastStore.sharedInstance().dismissToast(key);
             };
 
-            const onDismiss = () => {
+            const onDismiss = (): void => {
                 window.electron.send('userDownloadAction', { id });
             };
 
@@ -156,7 +156,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
         return this.ipc.call('getConfig');
     }
 
-    private onUpdateDownloaded = async (ev, { releaseNotes, releaseName }) => {
+    private onUpdateDownloaded = async (ev, { releaseNotes, releaseName }): Promise<void> => {
         dis.dispatch<CheckUpdatesPayload>({
             action: Action.CheckUpdates,
             status: UpdateCheckStatus.Ready,
@@ -223,7 +223,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
         );
 
         const handler = notification.onclick as Function;
-        notification.onclick = () => {
+        notification.onclick = (): void => {
             handler?.();
             this.ipc.call('focusWindow');
         };
@@ -231,7 +231,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
         return notification;
     }
 
-    public loudNotification(ev: MatrixEvent, room: Room) {
+    public loudNotification(ev: MatrixEvent, room: Room): void {
         window.electron.send('loudNotification');
     }
 
@@ -261,17 +261,17 @@ export default class ElectronPlatform extends VectorBasePlatform {
         return this.ipc.call("setSettingValue", settingName, value);
     }
 
-    async canSelfUpdate(): Promise<boolean> {
+    public async canSelfUpdate(): Promise<boolean> {
         const feedUrl = await this.ipc.call('getUpdateFeedUrl');
         return Boolean(feedUrl);
     }
 
-    public startUpdateCheck() {
+    public startUpdateCheck(): void {
         super.startUpdateCheck();
         window.electron.send('check_updates');
     }
 
-    public installUpdate() {
+    public installUpdate(): void {
         // IPC to the main process to install the update, since quitAndInstall
         // doesn't fire the before-quit event so the main process needs to know
         // it should exit.
@@ -290,7 +290,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
         return Promise.resolve('granted');
     }
 
-    public reload() {
+    public reload(): void {
         window.location.reload();
     }
 
@@ -298,7 +298,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
         return this.eventIndexManager;
     }
 
-    public async setLanguage(preferredLangs: string[]) {
+    public async setLanguage(preferredLangs: string[]): Promise<any> {
         return this.ipc.call('setLanguage', preferredLangs);
     }
 
@@ -353,7 +353,7 @@ export default class ElectronPlatform extends VectorBasePlatform {
         loginType: "sso" | "cas",
         fragmentAfterLogin: string,
         idpId?: string,
-    ) {
+    ): void {
         // this will get intercepted by electron-main will-navigate
         super.startSingleSignOn(mxClient, loginType, fragmentAfterLogin, idpId);
         Modal.createDialog(InfoDialog, {

+ 5 - 5
src/vector/platform/VectorBasePlatform.ts

@@ -43,7 +43,7 @@ export default abstract class VectorBasePlatform extends BasePlatform {
      * it uses canvas, which can trigger a permission prompt in Firefox's resist fingerprinting mode.
      * See https://github.com/vector-im/element-web/issues/9605.
      */
-    public get favicon() {
+    public get favicon(): Favicon {
         if (this._favicon) {
             return this._favicon;
         }
@@ -51,7 +51,7 @@ export default abstract class VectorBasePlatform extends BasePlatform {
         return this._favicon;
     }
 
-    private updateFavicon() {
+    private updateFavicon(): void {
         let bgColor = "#d00";
         let notif: string | number = this.notificationCount;
 
@@ -63,13 +63,13 @@ export default abstract class VectorBasePlatform extends BasePlatform {
         this.favicon.badge(notif, { bgColor });
     }
 
-    public setNotificationCount(count: number) {
+    public setNotificationCount(count: number): void {
         if (this.notificationCount === count) return;
         super.setNotificationCount(count);
         this.updateFavicon();
     }
 
-    public setErrorStatus(errorDidOccur: boolean) {
+    public setErrorStatus(errorDidOccur: boolean): void {
         if (this.errorDidOccur === errorDidOccur) return;
         super.setErrorStatus(errorDidOccur);
         this.updateFavicon();
@@ -78,7 +78,7 @@ export default abstract class VectorBasePlatform extends BasePlatform {
     /**
      * Begin update polling, if applicable
      */
-    public startUpdater() {
+    public startUpdater(): void {
     }
 
     /**

+ 1 - 1
src/vector/platform/WebPlatform.ts

@@ -40,7 +40,7 @@ function getNormalizedAppVersion(version: string): string {
 }
 
 export default class WebPlatform extends VectorBasePlatform {
-    constructor() {
+    public constructor() {
         super();
         // Register service worker if available on this platform
         if ('serviceWorker' in navigator) {

+ 3 - 3
src/vector/rageshakesetup.ts

@@ -31,7 +31,7 @@ import SdkConfig from "matrix-react-sdk/src/SdkConfig";
 import sendBugReport from "matrix-react-sdk/src/rageshake/submit-rageshake";
 import { logger } from "matrix-js-sdk/src/logger";
 
-export function initRageshake() {
+export function initRageshake(): Promise<void> {
     // we manually check persistence for rageshakes ourselves
     const prom = rageshake.init(/*setUpPersistence=*/false);
     prom.then(() => {
@@ -52,11 +52,11 @@ export function initRageshake() {
     return prom;
 }
 
-export function initRageshakeStore() {
+export function initRageshakeStore(): Promise<void> {
     return rageshake.tryInitStorage();
 }
 
-window.mxSendRageshake = function(text: string, withLogs?: boolean) {
+window.mxSendRageshake = function(text: string, withLogs?: boolean): void {
     const url = SdkConfig.get().bug_report_endpoint_url;
     if (!url) {
         logger.error("Cannot send a rageshake - no bug_report_endpoint_url configured");

+ 6 - 5
src/vector/routing.ts

@@ -17,13 +17,14 @@ limitations under the License.
 // Parse the given window.location and return parameters that can be used when calling
 // MatrixChat.showScreen(screen, params)
 import { logger } from "matrix-js-sdk/src/logger";
+import { QueryDict } from "matrix-js-sdk/src/utils";
 import MatrixChatType from "matrix-react-sdk/src/components/structures/MatrixChat";
 
 import { parseQsFromFragment } from "./url_utils";
 
 let lastLocationHashSet: string = null;
 
-export function getScreenFromLocation(location: Location) {
+export function getScreenFromLocation(location: Location): { screen: string, params: QueryDict } {
     const fragparts = parseQsFromFragment(location);
     return {
         screen: fragparts.location.substring(1),
@@ -33,7 +34,7 @@ export function getScreenFromLocation(location: Location) {
 
 // Here, we do some crude URL analysis to allow
 // deep-linking.
-function routeUrl(location: Location) {
+function routeUrl(location: Location): void {
     if (!window.matrixChat) return;
 
     logger.log("Routing URL ", location.href);
@@ -41,7 +42,7 @@ function routeUrl(location: Location) {
     (window.matrixChat as MatrixChatType).showScreen(s.screen, s.params);
 }
 
-function onHashChange() {
+function onHashChange(): void {
     if (decodeURIComponent(window.location.hash) === lastLocationHashSet) {
         // we just set this: no need to route it!
         return;
@@ -51,7 +52,7 @@ function onHashChange() {
 
 // This will be called whenever the SDK changes screens,
 // so a web page can update the URL bar appropriately.
-export function onNewScreen(screen: string, replaceLast = false) {
+export function onNewScreen(screen: string, replaceLast = false): void {
     logger.log("newscreen " + screen);
     const hash = '#/' + screen;
     lastLocationHashSet = hash;
@@ -71,6 +72,6 @@ export function onNewScreen(screen: string, replaceLast = false) {
     }
 }
 
-export function init() {
+export function init(): void {
     window.addEventListener('hashchange', onHashChange);
 }

+ 1 - 2
src/vector/url_utils.ts

@@ -19,8 +19,7 @@ import { QueryDict, decodeParams } from "matrix-js-sdk/src/utils";
 // We want to support some name / value pairs in the fragment
 // so we're re-using query string like format
 //
-// returns {location, params}
-export function parseQsFromFragment(location: Location) {
+export function parseQsFromFragment(location: Location): { location: string, params: QueryDict } {
     // if we have a fragment, it will start with '#', which we need to drop.
     // (if we don't, this will return '').
     const fragment = location.hash.substring(1);

+ 15 - 15
test/app-tests/loading-test.tsx

@@ -28,7 +28,7 @@ import MockHttpBackend from 'matrix-mock-request';
 import { makeType } from "matrix-react-sdk/src/utils/TypeUtils";
 import { ValidatedServerConfig } from 'matrix-react-sdk/src/utils/ValidatedServerConfig';
 import { IndexedDBCryptoStore } from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store";
-import { sleep } from "matrix-js-sdk/src/utils";
+import { QueryDict, sleep } from "matrix-js-sdk/src/utils";
 
 import "../jest-mocks";
 import WebPlatform from '../../src/vector/platform/WebPlatform';
@@ -84,7 +84,7 @@ describe('loading:', function() {
      * TODO: it would be nice to factor some of this stuff out of index.js so
      * that we can test it rather than our own implementation of it.
      */
-    function loadApp(opts?) {
+    function loadApp(opts?): void {
         opts = opts || {};
         const queryString = opts.queryString || "";
         const uriFragment = opts.uriFragment || "";
@@ -92,10 +92,10 @@ describe('loading:', function() {
         windowLocation = {
             search: queryString,
             hash: uriFragment,
-            toString: function() { return this.search + this.hash; },
+            toString: function(): string { return this.search + this.hash; },
         };
 
-        function onNewScreen(screen) {
+        function onNewScreen(screen): void {
             console.log(Date.now() + " newscreen "+screen);
             const hash = '#/' + screen;
             windowLocation.hash = hash;
@@ -104,7 +104,7 @@ describe('loading:', function() {
 
         // Parse the given window.location and return parameters that can be used when calling
         // MatrixChat.showScreen(screen, params)
-        function getScreenFromLocation(location) {
+        function getScreenFromLocation(location): { screen: string, params: QueryDict } {
             const fragparts = parseQsFromFragment(location);
             return {
                 screen: fragparts.location.substring(1),
@@ -143,7 +143,7 @@ describe('loading:', function() {
                     enableGuest={true}
                     onTokenLoginCompleted={resolve}
                     initialScreenAfterLogin={getScreenFromLocation(windowLocation)}
-                    makeRegistrationUrl={() => {throw new Error('Not implemented');}}
+                    makeRegistrationUrl={(): string => {throw new Error('Not implemented');}}
                 />, parentDiv,
             );
         });
@@ -153,7 +153,7 @@ describe('loading:', function() {
     // http requests until we do.
     //
     // returns a promise resolving to the received request
-    async function expectAndAwaitSync(opts?) {
+    async function expectAndAwaitSync(opts?): Promise<any> {
         let syncRequest = null;
         httpBackend.when('GET', '/_matrix/client/versions')
             .respond(200, {
@@ -548,7 +548,7 @@ describe('loading:', function() {
 
     // check that we have a Login component, send a 'user:pass' login,
     // and await the HTTP requests.
-    async function completeLogin(matrixChat: RenderResult) {
+    async function completeLogin(matrixChat: RenderResult): Promise<void> {
         // When we switch to the login component, it'll hit the login endpoint
         // for proof of life and to get flows. We'll only give it one option.
         httpBackend.when('GET', '/login')
@@ -587,15 +587,15 @@ describe('loading:', function() {
 });
 
 // assert that we are on the loading page
-async function assertAtLoadingSpinner() {
+async function assertAtLoadingSpinner(): Promise<void> {
     await screen.findByRole("progressbar");
 }
 
-async function awaitLoggedIn(matrixChat: RenderResult) {
+async function awaitLoggedIn(matrixChat: RenderResult): Promise<void> {
     if (matrixChat.container.querySelector(".mx_MatrixChat_wrapper")) return; // already logged in
 
     return new Promise(resolve => {
-        const onAction = ({ action }) => {
+        const onAction = ({ action }): void => {
             if (action !== "on_logged_in") {
                 return;
             }
@@ -608,19 +608,19 @@ async function awaitLoggedIn(matrixChat: RenderResult) {
     });
 }
 
-async function awaitRoomView(matrixChat: RenderResult) {
+async function awaitRoomView(matrixChat: RenderResult): Promise<void> {
     await waitFor(() => matrixChat.container.querySelector(".mx_RoomView"));
 }
 
-async function awaitLoginComponent(matrixChat: RenderResult) {
+async function awaitLoginComponent(matrixChat: RenderResult): Promise<void> {
     await waitFor(() => matrixChat.container.querySelector(".mx_AuthPage"));
 }
 
-async function awaitWelcomeComponent(matrixChat: RenderResult) {
+async function awaitWelcomeComponent(matrixChat: RenderResult): Promise<void> {
     await waitFor(() => matrixChat.container.querySelector(".mx_Welcome"));
 }
 
-function moveFromWelcomeToLogin(matrixChat: RenderResult) {
+function moveFromWelcomeToLogin(matrixChat: RenderResult): Promise<void> {
     dis.dispatch({ action: 'start_login' });
     return awaitLoginComponent(matrixChat);
 }

+ 3 - 3
test/test-utils.ts

@@ -29,17 +29,17 @@ export function deleteIndexedDB(dbName: string): Promise<void> {
         console.log(`${startTime}: Removing indexeddb instance: ${dbName}`);
         const req = window.indexedDB.deleteDatabase(dbName);
 
-        req.onblocked = () => {
+        req.onblocked = (): void => {
             console.log(`${Date.now()}: can't yet delete indexeddb ${dbName} because it is open elsewhere`);
         };
 
-        req.onerror = (ev) => {
+        req.onerror = (ev): void => {
             reject(new Error(
                 `${Date.now()}: unable to delete indexeddb ${dbName}: ${req.error}`,
             ));
         };
 
-        req.onsuccess = () => {
+        req.onsuccess = (): void => {
             const now = Date.now();
             console.log(`${now}: Removed indexeddb instance: ${dbName} in ${now-startTime} ms`);
             resolve();

+ 4 - 4
yarn.lock

@@ -5155,10 +5155,10 @@ eslint-plugin-import@^2.25.4:
     resolve "^1.22.0"
     tsconfig-paths "^3.14.1"
 
-eslint-plugin-matrix-org@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-0.7.0.tgz#4b7456b31e30e7575b62c2aada91915478829f88"
-  integrity sha512-FLmwE4/cRalB7J+J1BBuTccaXvKtRgAoHlbqSCbdsRqhh27xpxEWXe08KlNiET7drEnnz+xMHXdmvW469gch7g==
+eslint-plugin-matrix-org@^0.8.0:
+  version "0.8.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-0.8.0.tgz#daa1396900a8cb1c1d88f1a370e45fc32482cd9e"
+  integrity sha512-/Poz/F8lXYDsmQa29iPSt+kO+Jn7ArvRdq10g0CCk8wbRS0sb2zb6fvd9xL1BgR5UDQL771V0l8X32etvY5yKA==
 
 eslint-plugin-react-hooks@^4.3.0:
   version "4.6.0"