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

Commit 8c0d202

Browse files
authored
Enable tsc alwaysStrict, strictBindCallApply, noImplicitThis (#9600)
* Enable tsc alwaysStrict * Enable tsc strictBindCallApply * Enable tsc noImplicitThis * Add d.ts * Improve types * Add ? * Increase coverage * Improve coverage
1 parent 0b54699 commit 8c0d202

23 files changed

+188
-68
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@
212212
"stylelint": "^14.9.1",
213213
"stylelint-config-standard": "^26.0.0",
214214
"stylelint-scss": "^4.2.0",
215-
"typescript": "4.7.4",
215+
"typescript": "4.8.4",
216216
"walk": "^2.3.14"
217217
},
218218
"jest": {

src/@types/common.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,16 @@ export type Writeable<T> = { -readonly [P in keyof T]: T[P] };
2424
export type ComponentClass = keyof JSX.IntrinsicElements | JSXElementConstructor<any>;
2525
export type ReactAnyComponent = React.Component | React.ExoticComponent;
2626

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

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

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

3839
export type RecursivePartial<T> = {

src/@types/commonmark.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
Copyright 2022 The Matrix.org Foundation C.I.C.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
import * as commonmark from "commonmark";
18+
19+
declare module "commonmark" {
20+
export type Attr = [key: string, value: string];
21+
22+
export interface HtmlRenderer {
23+
// As far as @types/commonmark is concerned, these are not public, so add them
24+
// https://github.com/commonmark/commonmark.js/blob/master/lib/render/html.js#L272-L296
25+
text: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
26+
html_inline: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
27+
html_block: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
28+
// softbreak: () => void; // This one can't be correctly specified as it is wrongly defined in @types/commonmark
29+
linebreak: (this: commonmark.HtmlRenderer) => void;
30+
link: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
31+
image: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
32+
emph: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
33+
strong: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
34+
paragraph: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
35+
heading: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
36+
code: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
37+
code_block: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
38+
thematic_break: (this: commonmark.HtmlRenderer, node: commonmark.Node) => void;
39+
block_quote: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
40+
list: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
41+
item: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
42+
custom_inline: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
43+
custom_block: (this: commonmark.HtmlRenderer, node: commonmark.Node, entering: boolean) => void;
44+
esc: (s: string) => string;
45+
out: (this: commonmark.HtmlRenderer, text: string) => void;
46+
tag: (this: commonmark.HtmlRenderer, name: string, attrs?: Attr[], selfClosing?: boolean) => void;
47+
attrs: (this: commonmark.HtmlRenderer, node: commonmark.Node) => Attr[];
48+
// These are inherited from the base Renderer
49+
lit: (this: commonmark.HtmlRenderer, text: string) => void;
50+
cr: (this: commonmark.HtmlRenderer) => void;
51+
}
52+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.

src/Markdown.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ See the License for the specific language governing permissions and
1515
limitations under the License.
1616
*/
1717

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

29-
// As far as @types/commonmark is concerned, these are not public, so add them
30-
interface CommonmarkHtmlRendererInternal extends commonmark.HtmlRenderer {
31-
paragraph: (node: commonmark.Node, entering: boolean) => void;
32-
link: (node: commonmark.Node, entering: boolean) => void;
33-
html_inline: (node: commonmark.Node) => void; // eslint-disable-line camelcase
34-
html_block: (node: commonmark.Node) => void; // eslint-disable-line camelcase
35-
text: (node: commonmark.Node) => void;
36-
out: (text: string) => void;
37-
emph: (node: commonmark.Node) => void;
38-
}
39-
4030
function isAllowedHtmlTag(node: commonmark.Node): boolean {
4131
if (node.literal != null &&
4232
node.literal.match('^<((div|span) data-mx-maths="[^"]*"|/(div|span))>$') != null) {
@@ -248,7 +238,7 @@ export default class Markdown {
248238
isPlainText(): boolean {
249239
const walker = this.parsed.walker();
250240

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

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

361351
renderer.paragraph = function(node: commonmark.Node, entering: boolean) {
362352
// as with toHTML, only append lines to paragraphs if there are

src/Notifier.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ export const Notifier = {
210210
}
211211
},
212212

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

228-
stop: function() {
228+
stop: function(this: typeof Notifier) {
229229
if (MatrixClientPeg.get()) {
230230
MatrixClientPeg.get().removeListener(ClientEvent.Event, this.boundOnEvent);
231231
MatrixClientPeg.get().removeListener(RoomEvent.Receipt, this.boundOnRoomReceipt);
@@ -322,7 +322,7 @@ export const Notifier = {
322322
return SettingsStore.getValue("audioNotificationsEnabled");
323323
},
324324

325-
setPromptHidden: function(hidden: boolean, persistent = true) {
325+
setPromptHidden: function(this: typeof Notifier, hidden: boolean, persistent = true) {
326326
this.toolbarHidden = hidden;
327327

328328
hideNotificationsToast();
@@ -343,7 +343,7 @@ export const Notifier = {
343343
!this.isEnabled() && !this._isPromptHidden();
344344
},
345345

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

355-
onSyncStateChange: function(state: SyncState, prevState?: SyncState, data?: ISyncStateData) {
355+
onSyncStateChange: function(this: typeof Notifier, state: SyncState, prevState?: SyncState, data?: ISyncStateData) {
356356
if (state === SyncState.Syncing) {
357357
this.isSyncing = true;
358358
} else if (state === SyncState.Stopped || state === SyncState.Error) {
@@ -368,7 +368,7 @@ export const Notifier = {
368368
}
369369
},
370370

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

src/SlashCommands.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export const CommandCategories = {
111111

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

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

116116
interface ICommandOpts {
117117
command: string;
@@ -129,9 +129,9 @@ interface ICommandOpts {
129129
export class Command {
130130
public readonly command: string;
131131
public readonly aliases: string[];
132-
public readonly args: undefined | string;
132+
public readonly args?: string;
133133
public readonly description: string;
134-
public readonly runFn: undefined | RunFn;
134+
public readonly runFn?: RunFn;
135135
public readonly category: string;
136136
public readonly hideCompletionAfterSpace: boolean;
137137
public readonly renderingTypes?: TimelineRenderingType[];
@@ -143,7 +143,7 @@ export class Command {
143143
this.aliases = opts.aliases || [];
144144
this.args = opts.args || "";
145145
this.description = opts.description;
146-
this.runFn = opts.runFn;
146+
this.runFn = opts.runFn?.bind(this);
147147
this.category = opts.category || CommandCategories.other;
148148
this.hideCompletionAfterSpace = opts.hideCompletionAfterSpace || false;
149149
this._isEnabled = opts.isEnabled;
@@ -188,7 +188,7 @@ export class Command {
188188
});
189189
}
190190

191-
return this.runFn.bind(this)(roomId, args);
191+
return this.runFn(roomId, args);
192192
}
193193

194194
public getUsage() {
@@ -1114,7 +1114,7 @@ export const Commands = [
11141114
description: _td("Sends the given message coloured as a rainbow"),
11151115
args: '<message>',
11161116
runFn: function(roomId, args) {
1117-
if (!args) return reject(this.getUserId());
1117+
if (!args) return reject(this.getUsage());
11181118
return successSync(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args)));
11191119
},
11201120
category: CommandCategories.messages,
@@ -1124,7 +1124,7 @@ export const Commands = [
11241124
description: _td("Sends the given emote coloured as a rainbow"),
11251125
args: '<message>',
11261126
runFn: function(roomId, args) {
1127-
if (!args) return reject(this.getUserId());
1127+
if (!args) return reject(this.getUsage());
11281128
return successSync(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args)));
11291129
},
11301130
category: CommandCategories.messages,
@@ -1207,7 +1207,7 @@ export const Commands = [
12071207

12081208
return success((async () => {
12091209
if (isPhoneNumber) {
1210-
const results = await LegacyCallHandler.instance.pstnLookup(this.state.value);
1210+
const results = await LegacyCallHandler.instance.pstnLookup(userId);
12111211
if (!results || results.length === 0 || !results[0].userid) {
12121212
throw newTranslatableError("Unable to find Matrix ID for phone number");
12131213
}

src/autocomplete/QueryMatcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ interface IOptions<T extends {}> {
4545
* @param {function[]} options.funcs List of functions that when called with the
4646
* object as an arg will return a string to use as an index
4747
*/
48-
export default class QueryMatcher<T extends Object> {
48+
export default class QueryMatcher<T extends {}> {
4949
private _options: IOptions<T>;
5050
private _items: Map<string, {object: T, keyWeight: number}[]>;
5151

src/components/views/elements/Draggable.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default class Draggable extends React.Component<IProps, IState> {
4747
};
4848
}
4949

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

7676
render() {
77-
return <div className={this.props.className} onMouseDown={this.onMouseDown.bind(this)} />;
77+
return <div className={this.props.className} onMouseDown={this.onMouseDown} />;
7878
}
7979
}

src/components/views/elements/IRCTimelineProfileResizer.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I
4848
}, () => this.updateCSSWidth(this.state.width));
4949
}
5050

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

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

80-
private onMoueUp(event: MouseEvent) {
80+
private onMoueUp = () => {
8181
if (this.props.roomId) {
8282
SettingsStore.setValue(
8383
"ircDisplayNameWidth",
@@ -86,13 +86,13 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I
8686
this.state.width,
8787
);
8888
}
89-
}
89+
};
9090

9191
render() {
9292
return <Draggable
9393
className="mx_ProfileResizer"
94-
dragFunc={this.dragFunc.bind(this)}
95-
onMouseUp={this.onMoueUp.bind(this)}
94+
dragFunc={this.dragFunc}
95+
onMouseUp={this.onMoueUp}
9696
/>;
9797
}
9898
}

src/components/views/elements/Validation.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ See the License for the specific language governing permissions and
1515
limitations under the License.
1616
*/
1717

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

@@ -46,7 +45,7 @@ interface IArgs<T, D = void> {
4645
export interface IFieldState {
4746
value: string;
4847
focused: boolean;
49-
allowEmpty: boolean;
48+
allowEmpty?: boolean;
5049
}
5150

5251
export interface IValidationResult {
@@ -80,10 +79,13 @@ export interface IValidationResult {
8079
* A validation function that takes in the current input value and returns
8180
* the overall validity and a feedback UI that can be rendered for more detail.
8281
*/
83-
export default function withValidation<T = undefined, D = void>({
82+
export default function withValidation<T = void, D = void>({
8483
description, hideDescriptionIfValid, deriveData, rules,
8584
}: IArgs<T, D>) {
86-
return async function onValidate({ value, focused, allowEmpty = true }: IFieldState): Promise<IValidationResult> {
85+
return async function onValidate(
86+
this: T,
87+
{ value, focused, allowEmpty = true }: IFieldState,
88+
): Promise<IValidationResult> {
8789
if (!value && allowEmpty) {
8890
return {
8991
valid: null,
@@ -96,7 +98,7 @@ export default function withValidation<T = undefined, D = void>({
9698

9799
const results: IResult[] = [];
98100
let valid = true;
99-
if (rules && rules.length) {
101+
if (rules?.length) {
100102
for (const rule of rules) {
101103
if (!rule.key || !rule.test) {
102104
continue;

src/utils/Image.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ function arrayBufferReadInt(arr: ArrayBuffer, start: number): number {
3131
}
3232

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

3737
export async function blobIsAnimated(mimeType: string | undefined, blob: Blob): Promise<boolean> {

src/utils/MegolmExportEncryption.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ function packMegolmKeyFile(data: Uint8Array): ArrayBuffer {
356356
function encodeBase64(uint8Array: Uint8Array): string {
357357
// Misinterpt the Uint8Array as Latin-1.
358358
// window.btoa expects a unicode string with codepoints in the range 0-255.
359-
const latin1String = String.fromCharCode.apply(null, uint8Array);
359+
const latin1String = String.fromCharCode.apply(null, Array.from(uint8Array));
360360
// Use the builtin base64 encoder.
361361
return window.btoa(latin1String);
362362
}

test/ContentMessages-test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ describe("ContentMessages", () => {
131131
jest.spyOn(document, "createElement").mockImplementation(tagName => {
132132
const element = createElement(tagName);
133133
if (tagName === "video") {
134-
element.load = jest.fn();
135-
element.play = () => element.onloadeddata(new Event("loadeddata"));
136-
element.pause = jest.fn();
134+
(<HTMLVideoElement>element).load = jest.fn();
135+
(<HTMLVideoElement>element).play = () => element.onloadeddata(new Event("loadeddata"));
136+
(<HTMLVideoElement>element).pause = jest.fn();
137137
Object.defineProperty(element, 'videoHeight', {
138138
get() { return 600; },
139139
});

test/Notifier-test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,4 +433,11 @@ describe("Notifier", () => {
433433
expect(Notifier._displayPopupNotification).toHaveBeenCalledTimes(1);
434434
});
435435
});
436+
437+
describe("setPromptHidden", () => {
438+
it("should persist by default", () => {
439+
Notifier.setPromptHidden(true);
440+
expect(localStorage.getItem("notifications_hidden")).toBeTruthy();
441+
});
442+
});
436443
});

0 commit comments

Comments
 (0)