Make more code conform to strict null checks (#10219

* Make more code conform to strict null checks

* Fix types

* Fix tests

* Fix remaining test assertions

* Iterate PR
This commit is contained in:
Michael Telatynski 2023-02-24 15:28:40 +00:00 committed by GitHub
parent 4c79ecf141
commit 76b82b4b2b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
130 changed files with 603 additions and 603 deletions

View file

@ -46,7 +46,7 @@ interface IState {
export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
public static contextType = MatrixClientContext;
private unmounted = false;
private dispatcherRef: string = null;
private dispatcherRef: string | null = null;
public constructor(props: IProps, context: typeof MatrixClientContext) {
super(props, context);
@ -64,7 +64,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
let res: Response;
try {
res = await fetch(this.props.url, { method: "GET" });
res = await fetch(this.props.url!, { method: "GET" });
} catch (err) {
if (this.unmounted) return;
logger.warn(`Error loading page: ${err}`);
@ -84,7 +84,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
if (this.props.replaceMap) {
Object.keys(this.props.replaceMap).forEach((key) => {
body = body.split(key).join(this.props.replaceMap[key]);
body = body.split(key).join(this.props.replaceMap![key]);
});
}
@ -123,8 +123,7 @@ export default class EmbeddedPage extends React.PureComponent<IProps, IState> {
const client = this.context || MatrixClientPeg.get();
const isGuest = client ? client.isGuest() : true;
const className = this.props.className;
const classes = classnames({
[className]: true,
const classes = classnames(className, {
[`${className}_guest`]: isGuest,
[`${className}_loggedIn`]: !!client,
});

View file

@ -40,6 +40,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
const onDragEnter = (ev: DragEvent): void => {
ev.stopPropagation();
ev.preventDefault();
if (!ev.dataTransfer) return;
setState((state) => ({
// We always increment the counter no matter the types, because dragging is
@ -49,7 +50,8 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
// https://docs.w3cub.com/dom/datatransfer/types
// https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/Recommended_drag_types#file
dragging:
ev.dataTransfer.types.includes("Files") || ev.dataTransfer.types.includes("application/x-moz-file")
ev.dataTransfer!.types.includes("Files") ||
ev.dataTransfer!.types.includes("application/x-moz-file")
? true
: state.dragging,
}));
@ -68,6 +70,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
const onDragOver = (ev: DragEvent): void => {
ev.stopPropagation();
ev.preventDefault();
if (!ev.dataTransfer) return;
ev.dataTransfer.dropEffect = "none";
@ -82,6 +85,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
const onDrop = (ev: DragEvent): void => {
ev.stopPropagation();
ev.preventDefault();
if (!ev.dataTransfer) return;
onFileDrop(ev.dataTransfer);
setState((state) => ({

View file

@ -66,7 +66,7 @@ class FilePanel extends React.Component<IProps, IState> {
private onRoomTimeline = (
ev: MatrixEvent,
room: Room | null,
room: Room | undefined,
toStartOfTimeline: boolean,
removed: boolean,
data: IRoomTimelineData,
@ -78,7 +78,7 @@ class FilePanel extends React.Component<IProps, IState> {
client.decryptEventIfNeeded(ev);
if (ev.isBeingDecrypted()) {
this.decryptingEvents.add(ev.getId());
this.decryptingEvents.add(ev.getId()!);
} else {
this.addEncryptedLiveEvent(ev);
}
@ -86,7 +86,7 @@ class FilePanel extends React.Component<IProps, IState> {
private onEventDecrypted = (ev: MatrixEvent, err?: any): void => {
if (ev.getRoomId() !== this.props.roomId) return;
const eventId = ev.getId();
const eventId = ev.getId()!;
if (!this.decryptingEvents.delete(eventId)) return;
if (err) return;
@ -103,7 +103,7 @@ class FilePanel extends React.Component<IProps, IState> {
return;
}
if (!this.state.timelineSet.eventIdToTimeline(ev.getId())) {
if (!this.state.timelineSet.eventIdToTimeline(ev.getId()!)) {
this.state.timelineSet.addEventToTimeline(ev, timeline, false);
}
}

View file

@ -56,15 +56,15 @@ const getOwnProfile = (
userId: string,
): {
displayName: string;
avatarUrl: string;
avatarUrl?: string;
} => ({
displayName: OwnProfileStore.instance.displayName || userId,
avatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(AVATAR_SIZE),
avatarUrl: OwnProfileStore.instance.getHttpAvatarUrl(AVATAR_SIZE) ?? undefined,
});
const UserWelcomeTop: React.FC = () => {
const cli = useContext(MatrixClientContext);
const userId = cli.getUserId();
const userId = cli.getUserId()!;
const [ownProfile, setOwnProfile] = useState(getOwnProfile(userId));
useEventEmitter(OwnProfileStore.instance, UPDATE_EVENT, () => {
setOwnProfile(getOwnProfile(userId));

View file

@ -33,7 +33,7 @@ export const ERROR_USER_CANCELLED = new Error("User cancelled auth session");
type InteractiveAuthCallbackSuccess = (
success: true,
response: IAuthData,
response?: IAuthData,
extra?: { emailSid?: string; clientSecret?: string },
) => void;
type InteractiveAuthCallbackFailure = (success: false, response: IAuthData | Error) => void;
@ -94,7 +94,7 @@ interface IState {
export default class InteractiveAuthComponent extends React.Component<IProps, IState> {
private readonly authLogic: InteractiveAuth;
private readonly intervalId: number = null;
private readonly intervalId: number | null = null;
private readonly stageComponent = createRef<IStageComponent>();
private unmounted = false;
@ -103,10 +103,7 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
super(props);
this.state = {
authStage: null,
busy: false,
errorText: null,
errorCode: null,
submitButtonEnabled: false,
};
@ -213,8 +210,8 @@ export default class InteractiveAuthComponent extends React.Component<IProps, IS
if (busy) {
this.setState({
busy: true,
errorText: null,
errorCode: null,
errorText: undefined,
errorCode: undefined,
});
}
// The JS SDK eagerly reports itself as "not busy" right after any

View file

@ -166,10 +166,11 @@ export default class LeftPanel extends React.Component<IProps, IState> {
}
>();
let lastTopHeader;
let firstBottomHeader;
let lastTopHeader: HTMLDivElement | undefined;
let firstBottomHeader: HTMLDivElement | undefined;
for (const sublist of sublists) {
const header = sublist.querySelector<HTMLDivElement>(".mx_RoomSublist_stickable");
if (!header) continue; // this should never occur
header.style.removeProperty("display"); // always clear display:none first
// When an element is <=40% off screen, make it take over
@ -196,7 +197,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
// cause a no-op update, as adding/removing properties that are/aren't there cause
// layout updates.
for (const header of targetStyles.keys()) {
const style = targetStyles.get(header);
const style = targetStyles.get(header)!;
if (style.makeInvisible) {
// we will have already removed the 'display: none', so add it back.
@ -324,7 +325,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
}
private renderSearchDialExplore(): React.ReactNode {
let dialPadButton = null;
let dialPadButton: JSX.Element | undefined;
// If we have dialer support, show a button to bring up the dial pad
// to start a new call
@ -338,7 +339,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
);
}
let rightButton: JSX.Element;
let rightButton: JSX.Element | undefined;
if (this.state.showBreadcrumbs === BreadcrumbsMode.Labs) {
rightButton = <RecentlyViewedButton />;
} else if (this.state.activeSpace === MetaSpace.Home && shouldShowComponent(UIComponent.ExploreRooms)) {

View file

@ -139,15 +139,15 @@ export default class ViewSource extends React.Component<IProps, IState> {
private canSendStateEvent(mxEvent: MatrixEvent): boolean {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(mxEvent.getRoomId());
return room.currentState.mayClientSendStateEvent(mxEvent.getType(), cli);
return !!room?.currentState.mayClientSendStateEvent(mxEvent.getType(), cli);
}
public render(): React.ReactNode {
const mxEvent = this.props.mxEvent.replacingEvent() || this.props.mxEvent; // show the replacing event, not the original, if it is an edit
const isEditing = this.state.isEditing;
const roomId = mxEvent.getRoomId();
const eventId = mxEvent.getId();
const roomId = mxEvent.getRoomId()!;
const eventId = mxEvent.getId()!;
const canEdit = mxEvent.isState() ? this.canSendStateEvent(mxEvent) : canEditContent(this.props.mxEvent);
return (
<BaseDialog className="mx_ViewSource" onFinished={this.props.onFinished} title={_t("View Source")}>

View file

@ -39,6 +39,7 @@ import AuthBody from "../../views/auth/AuthBody";
import AuthHeader from "../../views/auth/AuthHeader";
import AccessibleButton, { ButtonEvent } from "../../views/elements/AccessibleButton";
import { ValidatedServerConfig } from "../../../utils/ValidatedServerConfig";
import { filterBoolean } from "../../../utils/arrays";
// These are used in several places, and come from the js-sdk's autodiscovery
// stuff. We define them here so that they'll be picked up by i18n.
@ -120,15 +121,11 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
this.state = {
busy: false,
busyLoggingIn: null,
errorText: null,
loginIncorrect: false,
canTryLogin: true,
flows: null,
username: props.defaultUsername ? props.defaultUsername : "",
phoneCountry: null,
phoneNumber: "",
serverIsAlive: true,
@ -167,7 +164,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
}
}
public isBusy = (): boolean => this.state.busy || this.props.busy;
public isBusy = (): boolean => !!this.state.busy || !!this.props.busy;
public onPasswordLogin: OnPasswordLogin = async (
username: string | undefined,
@ -349,7 +346,7 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
ev.preventDefault();
ev.stopPropagation();
const ssoKind = ssoFlow.type === "m.login.sso" ? "sso" : "cas";
PlatformPeg.get().startSingleSignOn(
PlatformPeg.get()?.startSingleSignOn(
this.loginLogic.createTemporaryClient(),
ssoKind,
this.props.fragmentAfterLogin,
@ -511,13 +508,13 @@ export default class LoginComponent extends React.PureComponent<IProps, IState>
return errorText;
}
public renderLoginComponentForFlows(): JSX.Element {
public renderLoginComponentForFlows(): ReactNode {
if (!this.state.flows) return null;
// this is the ideal order we want to show the flows in
const order = ["m.login.password", "m.login.sso"];
const flows = order.map((type) => this.state.flows.find((flow) => flow.type === type)).filter(Boolean);
const flows = filterBoolean(order.map((type) => this.state.flows.find((flow) => flow.type === type)));
return (
<React.Fragment>
{flows.map((flow) => {