Skip to content

Commit 4b8bada

Browse files
authored
Apply strictNullChecks around the codebase (matrix-org#10302
* Apply `strictNullChecks` around the codebase * Iterate PR
1 parent 7c2bb96 commit 4b8bada

26 files changed

+112
-77
lines changed

src/ContentMessages.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ async function infoForImageFile(
152152
// For lesser supported image types, always include the thumbnail even if it is larger
153153
if (!ALWAYS_INCLUDE_THUMBNAIL.includes(imageFile.type)) {
154154
// we do all sizing checks here because we still rely on thumbnail generation for making a blurhash from.
155-
const sizeDifference = imageFile.size - imageInfo.thumbnail_info.size;
155+
const sizeDifference = imageFile.size - imageInfo.thumbnail_info!.size;
156156
if (
157157
// image is small enough already
158158
imageFile.size <= IMAGE_SIZE_THRESHOLD_THUMBNAIL ||

src/DeviceListener.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,11 @@ export default class DeviceListener {
227227
// & cache the result
228228
private async getKeyBackupInfo(): Promise<IKeyBackupInfo | null> {
229229
const now = new Date().getTime();
230-
if (!this.keyBackupInfo || this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL) {
230+
if (
231+
!this.keyBackupInfo ||
232+
!this.keyBackupFetchedAt ||
233+
this.keyBackupFetchedAt < now - KEY_BACKUP_POLL_INTERVAL
234+
) {
231235
this.keyBackupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
232236
this.keyBackupFetchedAt = now;
233237
}
@@ -392,7 +396,7 @@ export default class DeviceListener {
392396
private updateClientInformation = async (): Promise<void> => {
393397
try {
394398
if (this.shouldRecordClientInformation) {
395-
await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get());
399+
await recordClientInformation(MatrixClientPeg.get(), SdkConfig.get(), PlatformPeg.get() ?? undefined);
396400
} else {
397401
await removeClientInformation(MatrixClientPeg.get());
398402
}

src/KeyBindingsDefaults.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { CATEGORIES, CategoryName, KeyBindingAction } from "./accessibility/Keyb
2323
import { getKeyboardShortcuts } from "./accessibility/KeyboardShortcutUtils";
2424

2525
export const getBindingsByCategory = (category: CategoryName): KeyBinding[] => {
26-
return CATEGORIES[category].settingNames.reduce((bindings, action) => {
26+
return CATEGORIES[category].settingNames.reduce<KeyBinding[]>((bindings, action) => {
2727
const keyCombo = getKeyboardShortcuts()[action]?.default;
2828
if (keyCombo) {
2929
bindings.push({ action, keyCombo });

src/NodeAnimator.tsx

+10-10
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414
limitations under the License.
1515
*/
1616

17-
import React, { ReactInstance } from "react";
17+
import React, { Key, ReactElement, ReactInstance } from "react";
1818
import ReactDom from "react-dom";
1919

2020
interface IChildProps {
@@ -42,7 +42,7 @@ interface IProps {
4242
*/
4343
export default class NodeAnimator extends React.Component<IProps> {
4444
private nodes: Record<string, ReactInstance> = {};
45-
private children: { [key: string]: React.DetailedReactHTMLElement<any, HTMLElement> };
45+
private children: { [key: string]: ReactElement };
4646
public static defaultProps: Partial<IProps> = {
4747
startStyles: [],
4848
};
@@ -72,17 +72,17 @@ export default class NodeAnimator extends React.Component<IProps> {
7272
private updateChildren(newChildren: React.ReactNode): void {
7373
const oldChildren = this.children || {};
7474
this.children = {};
75-
React.Children.toArray(newChildren).forEach((c: any) => {
76-
if (oldChildren[c.key]) {
77-
const old = oldChildren[c.key];
78-
const oldNode = ReactDom.findDOMNode(this.nodes[old.key]);
75+
React.Children.toArray(newChildren).forEach((c: ReactElement) => {
76+
if (oldChildren[c.key!]) {
77+
const old = oldChildren[c.key!];
78+
const oldNode = ReactDom.findDOMNode(this.nodes[old.key!]);
7979

8080
if (oldNode && (oldNode as HTMLElement).style.left !== c.props.style.left) {
8181
this.applyStyles(oldNode as HTMLElement, { left: c.props.style.left });
8282
}
8383
// clone the old element with the props (and children) of the new element
8484
// so prop updates are still received by the children.
85-
this.children[c.key] = React.cloneElement(old, c.props, c.props.children);
85+
this.children[c.key!] = React.cloneElement(old, c.props, c.props.children);
8686
} else {
8787
// new element. If we have a startStyle, use that as the style and go through
8888
// the enter animations
@@ -95,14 +95,14 @@ export default class NodeAnimator extends React.Component<IProps> {
9595
newProps.style = startStyle;
9696
}
9797

98-
newProps.ref = (n) => this.collectNode(c.key, n, restingStyle);
98+
newProps.ref = (n) => this.collectNode(c.key!, n, restingStyle);
9999

100-
this.children[c.key] = React.cloneElement(c, newProps);
100+
this.children[c.key!] = React.cloneElement(c, newProps);
101101
}
102102
});
103103
}
104104

105-
private collectNode(k: string, node: React.ReactInstance, restingStyle: React.CSSProperties): void {
105+
private collectNode(k: Key, node: React.ReactInstance, restingStyle: React.CSSProperties): void {
106106
if (node && this.nodes[k] === undefined && this.props.startStyles.length > 0) {
107107
const startStyles = this.props.startStyles;
108108
const domNode = ReactDom.findDOMNode(node);

src/Notifier.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,10 @@ class NotifierClass {
106106
private toolbarHidden?: boolean;
107107
private isSyncing?: boolean;
108108

109-
public notificationMessageForEvent(ev: MatrixEvent): string {
110-
if (msgTypeHandlers.hasOwnProperty(ev.getContent().msgtype)) {
111-
return msgTypeHandlers[ev.getContent().msgtype](ev);
109+
public notificationMessageForEvent(ev: MatrixEvent): string | null {
110+
const msgType = ev.getContent().msgtype;
111+
if (msgType && msgTypeHandlers.hasOwnProperty(msgType)) {
112+
return msgTypeHandlers[msgType](ev);
112113
}
113114
return TextForEvent.textForEvent(ev);
114115
}
@@ -134,9 +135,9 @@ class NotifierClass {
134135
let title;
135136
if (!ev.sender || room.name === ev.sender.name) {
136137
title = room.name;
137-
// notificationMessageForEvent includes sender,
138-
// but we already have the sender here
139-
if (ev.getContent().body && !msgTypeHandlers.hasOwnProperty(ev.getContent().msgtype)) {
138+
// notificationMessageForEvent includes sender, but we already have the sender here
139+
const msgType = ev.getContent().msgtype;
140+
if (ev.getContent().body && (!msgType || !msgTypeHandlers.hasOwnProperty(msgType))) {
140141
msg = ev.getContent().body;
141142
}
142143
} else if (ev.getType() === "m.room.member") {
@@ -145,9 +146,9 @@ class NotifierClass {
145146
title = room.name;
146147
} else if (ev.sender) {
147148
title = ev.sender.name + " (" + room.name + ")";
148-
// notificationMessageForEvent includes sender,
149-
// but we've just out sender in the title
150-
if (ev.getContent().body && !msgTypeHandlers.hasOwnProperty(ev.getContent().msgtype)) {
149+
// notificationMessageForEvent includes sender, but we've just out sender in the title
150+
const msgType = ev.getContent().msgtype;
151+
if (ev.getContent().body && (!msgType || !msgTypeHandlers.hasOwnProperty(msgType))) {
151152
msg = ev.getContent().body;
152153
}
153154
}
@@ -161,7 +162,7 @@ class NotifierClass {
161162
avatarUrl = Avatar.avatarUrlForMember(ev.sender, 40, 40, "crop");
162163
}
163164

164-
const notif = plaf.displayNotification(title, msg, avatarUrl, room, ev);
165+
const notif = plaf.displayNotification(title, msg!, avatarUrl, room, ev);
165166

166167
// if displayNotification returns non-null, the platform supports
167168
// clearing notifications later, so keep track of this.

src/Rooms.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616

1717
import { Room } from "matrix-js-sdk/src/models/room";
1818
import { EventType } from "matrix-js-sdk/src/@types/event";
19+
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
1920

2021
import { MatrixClientPeg } from "./MatrixClientPeg";
2122
import AliasCustomisations from "./customisations/Alias";
@@ -109,8 +110,8 @@ export async function setDMRoom(roomId: string, userId: string | null): Promise<
109110
* @returns {string} User ID of the user that the room is probably a DM with
110111
*/
111112
function guessDMRoomTargetId(room: Room, myUserId: string): string {
112-
let oldestTs;
113-
let oldestUser;
113+
let oldestTs: number | undefined;
114+
let oldestUser: RoomMember | undefined;
114115

115116
// Pick the joined user who's been here longest (and isn't us),
116117
for (const user of room.getJoinedMembers()) {

src/ScalarMessaging.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -858,19 +858,18 @@ async function readEvents(
858858

859859
const onMessage = function (event: MessageEvent<any>): void {
860860
if (!event.origin) {
861-
// stupid chrome
862-
// @ts-ignore
861+
// @ts-ignore - stupid chrome
863862
event.origin = event.originalEvent.origin;
864863
}
865864

866865
// Check that the integrations UI URL starts with the origin of the event
867866
// This means the URL could contain a path (like /develop) and still be used
868867
// to validate event origins, which do not specify paths.
869868
// (See https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage)
870-
let configUrl;
869+
let configUrl: URL | undefined;
871870
try {
872-
if (!openManagerUrl) openManagerUrl = IntegrationManagers.sharedInstance().getPrimaryManager().uiUrl;
873-
configUrl = new URL(openManagerUrl);
871+
if (!openManagerUrl) openManagerUrl = IntegrationManagers.sharedInstance().getPrimaryManager()?.uiUrl;
872+
configUrl = new URL(openManagerUrl!);
874873
} catch (e) {
875874
// No integrations UI URL, ignore silently.
876875
return;
@@ -987,7 +986,7 @@ const onMessage = function (event: MessageEvent<any>): void {
987986
};
988987

989988
let listenerCount = 0;
990-
let openManagerUrl: string | null = null;
989+
let openManagerUrl: string | undefined;
991990

992991
export function startListening(): void {
993992
if (listenerCount === 0) {

src/SecurityManager.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,10 @@ function makeInputToKey(keyInfo: ISecretStorageKeyInfo): (keyParams: KeyParams)
8787
return async ({ passphrase, recoveryKey }): Promise<Uint8Array> => {
8888
if (passphrase) {
8989
return deriveKey(passphrase, keyInfo.passphrase.salt, keyInfo.passphrase.iterations);
90-
} else {
90+
} else if (recoveryKey) {
9191
return decodeRecoveryKey(recoveryKey);
9292
}
93+
throw new Error("Invalid input, passphrase or recoveryKey need to be provided");
9394
};
9495
}
9596

src/SlashCommands.tsx

+21-5
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,10 @@ function successSync(value: any): RunResult {
208208

209209
const isCurrentLocalRoom = (): boolean => {
210210
const cli = MatrixClientPeg.get();
211-
const room = cli.getRoom(SdkContextClass.instance.roomViewStore.getRoomId());
211+
const roomId = SdkContextClass.instance.roomViewStore.getRoomId();
212+
if (!roomId) return false;
213+
const room = cli.getRoom(roomId);
214+
if (!room) return false;
212215
return isLocalRoom(room);
213216
};
214217

@@ -873,7 +876,9 @@ export const Commands = [
873876
description: _td("Define the power level of a user"),
874877
isEnabled(): boolean {
875878
const cli = MatrixClientPeg.get();
876-
const room = cli.getRoom(SdkContextClass.instance.roomViewStore.getRoomId());
879+
const roomId = SdkContextClass.instance.roomViewStore.getRoomId();
880+
if (!roomId) return false;
881+
const room = cli.getRoom(roomId);
877882
return (
878883
!!room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId()!) &&
879884
!isLocalRoom(room)
@@ -897,7 +902,10 @@ export const Commands = [
897902
);
898903
}
899904
const member = room.getMember(userId);
900-
if (!member || getEffectiveMembership(member.membership) === EffectiveMembership.Leave) {
905+
if (
906+
!member?.membership ||
907+
getEffectiveMembership(member.membership) === EffectiveMembership.Leave
908+
) {
901909
return reject(newTranslatableError("Could not find user in room"));
902910
}
903911
const powerLevelEvent = room.currentState.getStateEvents("m.room.power_levels", "");
@@ -916,7 +924,9 @@ export const Commands = [
916924
description: _td("Deops user with given id"),
917925
isEnabled(): boolean {
918926
const cli = MatrixClientPeg.get();
919-
const room = cli.getRoom(SdkContextClass.instance.roomViewStore.getRoomId());
927+
const roomId = SdkContextClass.instance.roomViewStore.getRoomId();
928+
if (!roomId) return false;
929+
const room = cli.getRoom(roomId);
920930
return (
921931
!!room?.currentState.maySendStateEvent(EventType.RoomPowerLevels, cli.getUserId()!) &&
922932
!isLocalRoom(room)
@@ -973,11 +983,12 @@ export const Commands = [
973983
// We use parse5, which doesn't render/create a DOM node. It instead runs
974984
// some superfast regex over the text so we don't have to.
975985
const embed = parseHtml(widgetUrl);
976-
if (embed && embed.childNodes && embed.childNodes.length === 1) {
986+
if (embed?.childNodes?.length === 1) {
977987
const iframe = embed.childNodes[0] as ChildElement;
978988
if (iframe.tagName.toLowerCase() === "iframe" && iframe.attrs) {
979989
const srcAttr = iframe.attrs.find((a) => a.name === "src");
980990
logger.log("Pulling URL out of iframe (embed code)");
991+
if (!srcAttr) return reject(newTranslatableError("iframe has no src attribute"));
981992
widgetUrl = srcAttr.value;
982993
}
983994
}
@@ -1240,6 +1251,7 @@ export const Commands = [
12401251
}
12411252

12421253
const roomId = await ensureDMExists(MatrixClientPeg.get(), userId);
1254+
if (!roomId) throw new Error("Failed to ensure DM exists");
12431255

12441256
dis.dispatch<ViewRoomPayload>({
12451257
action: Action.ViewRoom,
@@ -1267,6 +1279,8 @@ export const Commands = [
12671279
(async (): Promise<void> => {
12681280
const cli = MatrixClientPeg.get();
12691281
const roomId = await ensureDMExists(cli, userId);
1282+
if (!roomId) throw new Error("Failed to ensure DM exists");
1283+
12701284
dis.dispatch<ViewRoomPayload>({
12711285
action: Action.ViewRoom,
12721286
room_id: roomId,
@@ -1323,6 +1337,7 @@ export const Commands = [
13231337
isEnabled: () => !isCurrentLocalRoom(),
13241338
runFn: function (roomId, args) {
13251339
const room = MatrixClientPeg.get().getRoom(roomId);
1340+
if (!room) return reject(newTranslatableError("Could not find room"));
13261341
return success(guessAndSetDMRoom(room, true));
13271342
},
13281343
renderingTypes: [TimelineRenderingType.Room],
@@ -1334,6 +1349,7 @@ export const Commands = [
13341349
isEnabled: () => !isCurrentLocalRoom(),
13351350
runFn: function (roomId, args) {
13361351
const room = MatrixClientPeg.get().getRoom(roomId);
1352+
if (!room) return reject(newTranslatableError("Could not find room"));
13371353
return success(guessAndSetDMRoom(room, false));
13381354
},
13391355
renderingTypes: [TimelineRenderingType.Room],

src/Terms.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ export async function dialogTermsInteractionCallback(
201201
);
202202

203203
const [done, _agreedUrls] = await finished;
204-
if (!done) {
204+
if (!done || !_agreedUrls) {
205205
throw new TermsNotSignedError();
206206
}
207207
return _agreedUrls;

src/audio/PlaybackQueue.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ export class PlaybackQueue {
170170
// This should cause a Play event, which will re-populate our playback order
171171
// and update our current playback ID.
172172
// noinspection JSIgnoredPromiseFromCall
173-
instance.play();
173+
instance?.play();
174174
}
175175
}
176176
} else {

src/createRoom.ts

+1
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ export async function ensureDMExists(client: MatrixClient, userId: string): Prom
452452
}
453453

454454
roomId = await createRoom({ encryption, dmUserId: userId, spinner: false, andView: false });
455+
if (!roomId) return null;
455456
await waitForMember(client, roomId, userId);
456457
}
457458
return roomId;

src/hooks/usePublicRoomDirectory.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,9 @@ export const usePublicRoomDirectory = (): {
143143

144144
let roomServer: string = myHomeserver;
145145
if (
146-
SdkConfig.getObject("room_directory")?.get("servers")?.includes(lsRoomServer) ||
147-
SettingsStore.getValue("room_directory_servers")?.includes(lsRoomServer)
146+
lsRoomServer &&
147+
(SdkConfig.getObject("room_directory")?.get("servers")?.includes(lsRoomServer) ||
148+
SettingsStore.getValue("room_directory_servers")?.includes(lsRoomServer))
148149
) {
149150
roomServer = lsRoomServer!;
150151
}

src/hooks/useSpaceResults.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ import { normalize } from "matrix-js-sdk/src/utils";
2222

2323
import { MatrixClientPeg } from "../MatrixClientPeg";
2424

25-
export const useSpaceResults = (space?: Room, query?: string): [IHierarchyRoom[], boolean] => {
25+
export const useSpaceResults = (space: Room | undefined, query: string): [IHierarchyRoom[], boolean] => {
2626
const [rooms, setRooms] = useState<IHierarchyRoom[]>([]);
2727
const [hierarchy, setHierarchy] = useState<RoomHierarchy>();
2828

2929
const resetHierarchy = useCallback(() => {
30-
setHierarchy(space ? new RoomHierarchy(space, 50) : null);
30+
setHierarchy(space ? new RoomHierarchy(space, 50) : undefined);
3131
}, [space]);
3232
useEffect(resetHierarchy, [resetHierarchy]);
3333

@@ -40,7 +40,7 @@ export const useSpaceResults = (space?: Room, query?: string): [IHierarchyRoom[]
4040
while (hierarchy?.canLoadMore && !unmounted && space === hierarchy.root) {
4141
await hierarchy.load();
4242
if (hierarchy.canLoadMore) hierarchy.load(); // start next load so that the loading attribute is right
43-
setRooms(hierarchy.rooms);
43+
setRooms(hierarchy.rooms!);
4444
}
4545
})();
4646

src/i18n/strings/en_EN.json

+2
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@
453453
"Opens the Developer Tools dialog": "Opens the Developer Tools dialog",
454454
"Adds a custom widget by URL to the room": "Adds a custom widget by URL to the room",
455455
"Please supply a widget URL or embed code": "Please supply a widget URL or embed code",
456+
"iframe has no src attribute": "iframe has no src attribute",
456457
"Please supply a https:// or http:// widget URL": "Please supply a https:// or http:// widget URL",
457458
"You cannot modify widgets in this room.": "You cannot modify widgets in this room.",
458459
"Verifies a user, session, and pubkey tuple": "Verifies a user, session, and pubkey tuple",
@@ -478,6 +479,7 @@
478479
"No active call in this room": "No active call in this room",
479480
"Takes the call in the current room off hold": "Takes the call in the current room off hold",
480481
"Converts the room to a DM": "Converts the room to a DM",
482+
"Could not find room": "Could not find room",
481483
"Converts the DM to a room": "Converts the DM to a room",
482484
"Displays action": "Displays action",
483485
"Someone": "Someone",

0 commit comments

Comments
 (0)