Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Commit

Permalink
Enable tsc alwaysStrict, strictBindCallApply, noImplicitThis (#9600)
Browse files Browse the repository at this point in the history
* Enable tsc alwaysStrict

* Enable tsc strictBindCallApply

* Enable tsc noImplicitThis

* Add d.ts

* Improve types

* Add ?

* Increase coverage

* Improve coverage
  • Loading branch information
t3chguy authored Nov 21, 2022
1 parent 0b54699 commit 8c0d202
Show file tree
Hide file tree
Showing 23 changed files with 188 additions and 68 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@
"stylelint": "^14.9.1",
"stylelint-config-standard": "^26.0.0",
"stylelint-scss": "^4.2.0",
"typescript": "4.7.4",
"typescript": "4.8.4",
"walk": "^2.3.14"
},
"jest": {
Expand Down
5 changes: 3 additions & 2 deletions src/@types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ export type Writeable<T> = { -readonly [P in keyof T]: T[P] };
export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor<any>;
export type ReactAnyComponent = React.Component | React.ExoticComponent;

// Utility type for string dot notation for accessing nested object properties
// Based on https://stackoverflow.com/a/58436959
type Join<K, P> = K extends string | number ?
P extends string | number ?
`${K}${"" extends P ? "" : "."}${P}`
: never : never;

type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...0[]];
type Prev = [never, 0, 1, 2, 3, ...0[]];

export type Leaves<T, D extends number = 5> = [D] extends [never] ? never : T extends object ?
export type Leaves<T, D extends number = 3> = [D] extends [never] ? never : T extends object ?
{ [K in keyof T]-?: Join<K, Leaves<T[K], Prev[D]>> }[keyof T] : "";

export type RecursivePartial<T> = {
Expand Down
52 changes: 52 additions & 0 deletions src/@types/commonmark.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import * as commonmark from "commonmark";

declare module "commonmark" {
export type Attr = [key: string, value: string];

export interface HtmlRenderer {
// As far as @types/commonmark is concerned, these are not public, so add them
// https://github.com/commonmark/commonmark.js/blob/master/lib/render/html.js#L272-L296
text: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
html_inline: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
html_block: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
// softbreak: () => void; // This one can't be correctly specified as it is wrongly defined in @types/commonmark
linebreak: (this: commonmark.HtmlRenderer) => void;
link: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
image: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
emph: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
strong: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
paragraph: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
heading: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
code: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
code_block: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
thematic_break: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
block_quote: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
list: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
item: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
custom_inline: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
custom_block: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
esc: (s: string) => string;
out: (this: commonmark.HtmlRenderer, text: string) => void;
tag: (this: commonmark.HtmlRenderer, name: string, attrs?: Attr[], selfClosing?: boolean) => void;
attrs: (this: commonmark.HtmlRenderer, node: commonmark.Node) => Attr[];
// These are inherited from the base Renderer
lit: (this: commonmark.HtmlRenderer, text: string) => void;
cr: (this: commonmark.HtmlRenderer) => void;
}
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
18 changes: 4 additions & 14 deletions src/Markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

import "./@types/commonmark"; // import better types than @types/commonmark
import * as commonmark from 'commonmark';
import { escape } from "lodash";
import { logger } from 'matrix-js-sdk/src/logger';
Expand All @@ -26,17 +27,6 @@ const ALLOWED_HTML_TAGS = ['sub', 'sup', 'del', 'u'];
// These types of node are definitely text
const TEXT_NODES = ['text', 'softbreak', 'linebreak', 'paragraph', 'document'];

// As far as @types/commonmark is concerned, these are not public, so add them
interface CommonmarkHtmlRendererInternal extends commonmark.HtmlRenderer {
paragraph: (node: commonmark.Node, entering: boolean) => void;
link: (node: commonmark.Node, entering: boolean) => void;
html_inline: (node: commonmark.Node) => void; // eslint-disable-line camelcase
html_block: (node: commonmark.Node) => void; // eslint-disable-line camelcase
text: (node: commonmark.Node) => void;
out: (text: string) => void;
emph: (node: commonmark.Node) => void;
}

function isAllowedHtmlTag(node: commonmark.Node): boolean {
if (node.literal != null &&
node.literal.match('^<((div|span) data-mx-maths="[^"]*"|/(div|span))>$') != null) {
Expand Down Expand Up @@ -248,7 +238,7 @@ export default class Markdown {
isPlainText(): boolean {
const walker = this.parsed.walker();

let ev;
let ev: commonmark.NodeWalkingStep;
while (ev = walker.next()) {
const node = ev.node;
if (TEXT_NODES.indexOf(node.type) > -1) {
Expand Down Expand Up @@ -278,7 +268,7 @@ export default class Markdown {
// block quote ends up all on one line
// (https://github.com/vector-im/element-web/issues/3154)
softbreak: '<br />',
}) as CommonmarkHtmlRendererInternal;
});

// Trying to strip out the wrapping <p/> causes a lot more complication
// than it's worth, i think. For instance, this code will go and strip
Expand Down Expand Up @@ -356,7 +346,7 @@ export default class Markdown {
* which has no formatting. Otherwise it emits HTML(!).
*/
toPlaintext(): string {
const renderer = new commonmark.HtmlRenderer({ safe: false }) as CommonmarkHtmlRendererInternal;
const renderer = new commonmark.HtmlRenderer({ safe: false });

renderer.paragraph = function(node: commonmark.Node, entering: boolean) {
// as with toHTML, only append lines to paragraphs if there are
Expand Down
12 changes: 6 additions & 6 deletions src/Notifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export const Notifier = {
}
},

start: function() {
start: function(this: typeof Notifier) {
// do not re-bind in the case of repeated call
this.boundOnEvent = this.boundOnEvent || this.onEvent.bind(this);
this.boundOnSyncStateChange = this.boundOnSyncStateChange || this.onSyncStateChange.bind(this);
Expand All @@ -225,7 +225,7 @@ export const Notifier = {
this.isSyncing = false;
},

stop: function() {
stop: function(this: typeof Notifier) {
if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener(ClientEvent.Event, this.boundOnEvent);
MatrixClientPeg.get().removeListener(RoomEvent.Receipt, this.boundOnRoomReceipt);
Expand Down Expand Up @@ -322,7 +322,7 @@ export const Notifier = {
return SettingsStore.getValue("audioNotificationsEnabled");
},

setPromptHidden: function(hidden: boolean, persistent = true) {
setPromptHidden: function(this: typeof Notifier, hidden: boolean, persistent = true) {
this.toolbarHidden = hidden;

hideNotificationsToast();
Expand All @@ -343,7 +343,7 @@ export const Notifier = {
!this.isEnabled() && !this._isPromptHidden();
},

_isPromptHidden: function() {
_isPromptHidden: function(this: typeof Notifier) {
// Check localStorage for any such meta data
if (global.localStorage) {
return global.localStorage.getItem("notifications_hidden") === "true";
Expand All @@ -352,7 +352,7 @@ export const Notifier = {
return this.toolbarHidden;
},

onSyncStateChange: function(state: SyncState, prevState?: SyncState, data?: ISyncStateData) {
onSyncStateChange: function(this: typeof Notifier, state: SyncState, prevState?: SyncState, data?: ISyncStateData) {
if (state === SyncState.Syncing) {
this.isSyncing = true;
} else if (state === SyncState.Stopped || state === SyncState.Error) {
Expand All @@ -368,7 +368,7 @@ export const Notifier = {
}
},

onEvent: function(ev: MatrixEvent) {
onEvent: function(this: typeof Notifier, ev: MatrixEvent) {
if (!this.isSyncing) return; // don't alert for any messages initially
if (ev.getSender() === MatrixClientPeg.get().getUserId()) return;

Expand Down
16 changes: 8 additions & 8 deletions src/SlashCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export const CommandCategories = {

export type RunResult = XOR<{ error: Error | ITranslatableError }, { promise: Promise<IContent | undefined> }>;

type RunFn = ((roomId: string, args: string, cmd: string) => RunResult);
type RunFn = ((this: Command, roomId: string, args: string) => RunResult);

interface ICommandOpts {
command: string;
Expand All @@ -129,9 +129,9 @@ interface ICommandOpts {
export class Command {
public readonly command: string;
public readonly aliases: string[];
public readonly args: undefined | string;
public readonly args?: string;
public readonly description: string;
public readonly runFn: undefined | RunFn;
public readonly runFn?: RunFn;
public readonly category: string;
public readonly hideCompletionAfterSpace: boolean;
public readonly renderingTypes?: TimelineRenderingType[];
Expand All @@ -143,7 +143,7 @@ export class Command {
this.aliases = opts.aliases || [];
this.args = opts.args || "";
this.description = opts.description;
this.runFn = opts.runFn;
this.runFn = opts.runFn?.bind(this);
this.category = opts.category || CommandCategories.other;
this.hideCompletionAfterSpace = opts.hideCompletionAfterSpace || false;
this._isEnabled = opts.isEnabled;
Expand Down Expand Up @@ -188,7 +188,7 @@ export class Command {
});
}

return this.runFn.bind(this)(roomId, args);
return this.runFn(roomId, args);
}

public getUsage() {
Expand Down Expand Up @@ -1114,7 +1114,7 @@ export const Commands = [
description: _td("Sends the given message coloured as a rainbow"),
args: '<message>',
runFn: function(roomId, args) {
if (!args) return reject(this.getUserId());
if (!args) return reject(this.getUsage());
return successSync(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args)));
},
category: CommandCategories.messages,
Expand All @@ -1124,7 +1124,7 @@ export const Commands = [
description: _td("Sends the given emote coloured as a rainbow"),
args: '<message>',
runFn: function(roomId, args) {
if (!args) return reject(this.getUserId());
if (!args) return reject(this.getUsage());
return successSync(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args)));
},
category: CommandCategories.messages,
Expand Down Expand Up @@ -1207,7 +1207,7 @@ export const Commands = [

return success((async () => {
if (isPhoneNumber) {
const results = await LegacyCallHandler.instance.pstnLookup(this.state.value);
const results = await LegacyCallHandler.instance.pstnLookup(userId);
if (!results || results.length === 0 || !results[0].userid) {
throw newTranslatableError("Unable to find Matrix ID for phone number");
}
Expand Down
2 changes: 1 addition & 1 deletion src/autocomplete/QueryMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ interface IOptions<T extends {}> {
* @param {function[]} options.funcs List of functions that when called with the
* object as an arg will return a string to use as an index
*/
export default class QueryMatcher<T extends Object> {
export default class QueryMatcher<T extends {}> {
private _options: IOptions<T>;
private _items: Map<string, {object: T, keyWeight: number}[]>;

Expand Down
4 changes: 2 additions & 2 deletions src/components/views/elements/Draggable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default class Draggable extends React.Component<IProps, IState> {
};
}

private onMouseDown = (event: MouseEvent): void => {
private onMouseDown = (event: React.MouseEvent): void => {
this.setState({
location: {
currentX: event.clientX,
Expand All @@ -74,6 +74,6 @@ export default class Draggable extends React.Component<IProps, IState> {
}

render() {
return <div className={this.props.className} onMouseDown={this.onMouseDown.bind(this)} />;
return <div className={this.props.className} onMouseDown={this.onMouseDown} />;
}
}
10 changes: 5 additions & 5 deletions src/components/views/elements/IRCTimelineProfileResizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I
}, () => this.updateCSSWidth(this.state.width));
}

private dragFunc = (location: ILocationState, event: React.MouseEvent<Element, MouseEvent>): ILocationState => {
private dragFunc = (location: ILocationState, event: MouseEvent): ILocationState => {
const offset = event.clientX - location.currentX;
const newWidth = this.state.width + offset;

Expand Down Expand Up @@ -77,7 +77,7 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I
this.state.IRCLayoutRoot.style.setProperty("--name-width", newWidth + "px");
}

private onMoueUp(event: MouseEvent) {
private onMoueUp = () => {
if (this.props.roomId) {
SettingsStore.setValue(
"ircDisplayNameWidth",
Expand All @@ -86,13 +86,13 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I
this.state.width,
);
}
}
};

render() {
return <Draggable
className="mx_ProfileResizer"
dragFunc={this.dragFunc.bind(this)}
onMouseUp={this.onMoueUp.bind(this)}
dragFunc={this.dragFunc}
onMouseUp={this.onMoueUp}
/>;
}
}
12 changes: 7 additions & 5 deletions src/components/views/elements/Validation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

/* eslint-disable @typescript-eslint/no-invalid-this */
import React from "react";
import classNames from "classnames";

Expand Down Expand Up @@ -46,7 +45,7 @@ interface IArgs<T, D = void> {
export interface IFieldState {
value: string;
focused: boolean;
allowEmpty: boolean;
allowEmpty?: boolean;
}

export interface IValidationResult {
Expand Down Expand Up @@ -80,10 +79,13 @@ export interface IValidationResult {
* A validation function that takes in the current input value and returns
* the overall validity and a feedback UI that can be rendered for more detail.
*/
export default function withValidation<T = undefined, D = void>({
export default function withValidation<T = void, D = void>({
description, hideDescriptionIfValid, deriveData, rules,
}: IArgs<T, D>) {
return async function onValidate({ value, focused, allowEmpty = true }: IFieldState): Promise<IValidationResult> {
return async function onValidate(
this: T,
{ value, focused, allowEmpty = true }: IFieldState,
): Promise<IValidationResult> {
if (!value && allowEmpty) {
return {
valid: null,
Expand All @@ -96,7 +98,7 @@ export default function withValidation<T = undefined, D = void>({

const results: IResult[] = [];
let valid = true;
if (rules && rules.length) {
if (rules?.length) {
for (const rule of rules) {
if (!rule.key || !rule.test) {
continue;
Expand Down
2 changes: 1 addition & 1 deletion src/utils/Image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ function arrayBufferReadInt(arr: ArrayBuffer, start: number): number {
}

function arrayBufferReadStr(arr: ArrayBuffer, start: number, len: number): string {
return String.fromCharCode.apply(null, arrayBufferRead(arr, start, len));
return String.fromCharCode.apply(null, Array.from(arrayBufferRead(arr, start, len)));
}

export async function blobIsAnimated(mimeType: string | undefined, blob: Blob): Promise<boolean> {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/MegolmExportEncryption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ function packMegolmKeyFile(data: Uint8Array): ArrayBuffer {
function encodeBase64(uint8Array: Uint8Array): string {
// Misinterpt the Uint8Array as Latin-1.
// window.btoa expects a unicode string with codepoints in the range 0-255.
const latin1String = String.fromCharCode.apply(null, uint8Array);
const latin1String = String.fromCharCode.apply(null, Array.from(uint8Array));
// Use the builtin base64 encoder.
return window.btoa(latin1String);
}
Expand Down
6 changes: 3 additions & 3 deletions test/ContentMessages-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ describe("ContentMessages", () => {
jest.spyOn(document, "createElement").mockImplementation(tagName => {
const element = createElement(tagName);
if (tagName === "video") {
element.load = jest.fn();
element.play = () => element.onloadeddata(new Event("loadeddata"));
element.pause = jest.fn();
(<HTMLVideoElement>element).load = jest.fn();
(<HTMLVideoElement>element).play = () => element.onloadeddata(new Event("loadeddata"));
(<HTMLVideoElement>element).pause = jest.fn();
Object.defineProperty(element, 'videoHeight', {
get() { return 600; },
});
Expand Down
7 changes: 7 additions & 0 deletions test/Notifier-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,4 +433,11 @@ describe("Notifier", () => {
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
});
});

describe("setPromptHidden", () => {
it("should persist by default", () => {
Notifier.setPromptHidden(true);
expect(localStorage.getItem("notifications_hidden")).toBeTruthy();
});
});
});
Loading

0 comments on commit 8c0d202

Please sign in to comment.