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

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { ReactNode } from "react";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { logger } from "matrix-js-sdk/src/logger";
@ -79,13 +79,13 @@ export default class BridgeTile extends React.PureComponent<IProps> {
`Bridge info event ${this.props.ev.getId()} does not provide a 'bridgebot' key which` +
"is deprecated behaviour. Using sender for now.",
);
content.bridgebot = this.props.ev.getSender();
content.bridgebot = this.props.ev.getSender()!;
}
const { channel, network, protocol } = content;
const protocolName = protocol.displayname || protocol.id;
const channelName = channel.displayname || channel.id;
let creator = null;
let creator: JSX.Element | undefined;
if (content.creator) {
creator = (
<li>
@ -129,7 +129,7 @@ export default class BridgeTile extends React.PureComponent<IProps> {
let networkIcon;
if (protocol.avatar_url) {
const avatarUrl = mediaFromMxc(protocol.avatar_url).getSquareThumbnailHttp(64);
const avatarUrl = mediaFromMxc(protocol.avatar_url).getSquareThumbnailHttp(64) ?? undefined;
networkIcon = (
<BaseAvatar
@ -145,7 +145,7 @@ export default class BridgeTile extends React.PureComponent<IProps> {
} else {
networkIcon = <div className="mx_RoomSettingsDialog_noProtocolIcon" />;
}
let networkItem = null;
let networkItem: ReactNode | undefined;
if (network) {
const networkName = network.displayname || network.id;
let networkLink = <span>{networkName}</span>;

View file

@ -24,8 +24,8 @@ export default class ChangeDisplayName extends React.Component {
private getDisplayName = async (): Promise<string> => {
const cli = MatrixClientPeg.get();
try {
const res = await cli.getProfileInfo(cli.getUserId());
return res.displayname;
const res = await cli.getProfileInfo(cli.getUserId()!);
return res.displayname ?? "";
} catch (e) {
throw new Error("Failed to fetch display name");
}

View file

@ -42,12 +42,12 @@ enum Phase {
}
interface IProps {
onFinished?: (outcome: {
onFinished: (outcome: {
didSetEmail?: boolean;
/** Was one or more other devices logged out whilst changing the password */
didLogoutOutOtherDevices: boolean;
}) => void;
onError?: (error: { error: string }) => void;
onError: (error: { error: string }) => void;
rowClassName?: string;
buttonClassName?: string;
buttonKind?: string;
@ -68,9 +68,9 @@ interface IState {
}
export default class ChangePassword extends React.Component<IProps, IState> {
private [FIELD_OLD_PASSWORD]: Field;
private [FIELD_NEW_PASSWORD]: Field;
private [FIELD_NEW_PASSWORD_CONFIRM]: Field;
private [FIELD_OLD_PASSWORD]: Field | null;
private [FIELD_NEW_PASSWORD]: Field | null;
private [FIELD_NEW_PASSWORD_CONFIRM]: Field | null;
public static defaultProps: Partial<IProps> = {
onFinished() {},
@ -154,7 +154,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
},
// TODO: Remove `user` once servers support proper UIA
// See https://github.com/matrix-org/synapse/issues/5665
user: cli.credentials.userId,
user: cli.credentials.userId ?? undefined,
password: oldPassword,
};
@ -195,7 +195,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
});
}
private checkPassword(oldPass: string, newPass: string, confirmPass: string): { error: string } {
private checkPassword(oldPass: string, newPass: string, confirmPass: string): { error: string } | undefined {
if (newPass !== confirmPass) {
return {
error: _t("New passwords don't match"),
@ -226,7 +226,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
);
};
private markFieldValid(fieldID: FieldType, valid: boolean): void {
private markFieldValid(fieldID: FieldType, valid?: boolean): void {
const { fieldValid } = this.state;
fieldValid[fieldID] = valid;
this.setState({
@ -367,7 +367,7 @@ export default class ChangePassword extends React.Component<IProps, IState> {
return Object.values(this.state.fieldValid).every(Boolean);
}
private findFirstInvalidField(fieldIDs: FieldType[]): Field {
private findFirstInvalidField(fieldIDs: FieldType[]): Field | null {
for (const fieldID of fieldIDs) {
if (!this.state.fieldValid[fieldID] && this[fieldID]) {
return this[fieldID];

View file

@ -75,7 +75,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
private onBootstrapClick = (): void => {
if (this.state.crossSigningPrivateKeysInStorage) {
Modal.createDialog(SetupEncryptionDialog, {}, null, /* priority = */ false, /* static = */ true);
Modal.createDialog(SetupEncryptionDialog, {}, undefined, /* priority = */ false, /* static = */ true);
} else {
// Trigger the flow to set up secure backup, which is what this will do when in
// the appropriate state.
@ -90,8 +90,8 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
private async getUpdatedStatus(): Promise<void> {
const cli = MatrixClientPeg.get();
const pkCache = cli.getCrossSigningCacheCallbacks();
const crossSigning = cli.crypto.crossSigningInfo;
const secretStorage = cli.crypto.secretStorage;
const crossSigning = cli.crypto!.crossSigningInfo;
const secretStorage = cli.crypto!.secretStorage;
const crossSigningPublicKeysOnDevice = Boolean(crossSigning.getId());
const crossSigningPrivateKeysInStorage = Boolean(await crossSigning.isStoredInSecretStorage(secretStorage));
const masterPrivateKeyCached = !!(pkCache && (await pkCache.getCrossSigningKeyCache("master")));
@ -209,7 +209,7 @@ export default class CrossSigningPanel extends React.PureComponent<{}, IState> {
selfSigningPrivateKeyCached &&
userSigningPrivateKeyCached;
const actions = [];
const actions: JSX.Element[] = [];
// TODO: determine how better to expose this to users in addition to prompts at login/toast
if (!keysExistEverywhere && homeserverSupportsCrossSigning) {

View file

@ -64,7 +64,7 @@ export default class DevicesPanel extends React.Component<IProps, IState> {
}
private onDevicesUpdated = (users: string[]): void => {
if (!users.includes(this.context.getUserId())) return;
if (!users.includes(this.context.getUserId()!)) return;
this.loadDevices();
};
@ -252,9 +252,9 @@ export default class DevicesPanel extends React.Component<IProps, IState> {
const otherDevices = devices.filter((device) => device.device_id !== myDeviceId);
otherDevices.sort(this.deviceCompare);
const verifiedDevices = [];
const unverifiedDevices = [];
const nonCryptoDevices = [];
const verifiedDevices: IMyDevice[] = [];
const unverifiedDevices: IMyDevice[] = [];
const nonCryptoDevices: IMyDevice[] = [];
for (const device of otherDevices) {
const verified = this.isDeviceVerified(device);
if (verified === true) {
@ -271,7 +271,7 @@ export default class DevicesPanel extends React.Component<IProps, IState> {
return <React.Fragment />;
}
let selectButton: JSX.Element;
let selectButton: JSX.Element | undefined;
if (deviceList.length > 1) {
const anySelected = deviceList.some((device) => this.state.selectedDevices.includes(device.device_id));
const buttonAction = anySelected

View file

@ -103,7 +103,7 @@ export default class DevicesPanelEntry extends React.Component<IProps, IState> {
});
} else {
const cli = MatrixClientPeg.get();
const userId = cli.getUserId();
const userId = cli.getUserId()!;
const verificationRequestPromise = cli.requestVerification(userId, [this.props.device.device_id]);
Modal.createDialog(VerificationRequestDialog, {
verificationRequestPromise,
@ -119,7 +119,7 @@ export default class DevicesPanelEntry extends React.Component<IProps, IState> {
public render(): React.ReactNode {
let iconClass = "";
let verifyButton: JSX.Element;
let verifyButton: JSX.Element | undefined;
if (this.props.verified !== null) {
iconClass = this.props.verified ? "mx_E2EIcon_verified" : "mx_E2EIcon_warning";
if (!this.props.verified && this.props.canBeVerified) {
@ -131,7 +131,7 @@ export default class DevicesPanelEntry extends React.Component<IProps, IState> {
}
}
let signOutButton: JSX.Element;
let signOutButton: JSX.Element | undefined;
if (this.props.isOwnDevice) {
signOutButton = (
<AccessibleButton kind="danger_outline" onClick={this.onOwnDeviceSignOut}>

View file

@ -26,6 +26,7 @@ import EventIndexPeg from "../../../indexing/EventIndexPeg";
import { SettingLevel } from "../../../settings/SettingLevel";
import SeshatResetDialog from "../dialogs/SeshatResetDialog";
import InlineSpinner from "../elements/InlineSpinner";
import { IIndexStats } from "../../../indexing/BaseEventIndexManager";
interface IState {
enabling: boolean;
@ -48,10 +49,10 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
public updateCurrentRoom = async (): Promise<void> => {
const eventIndex = EventIndexPeg.get();
let stats;
let stats: IIndexStats | undefined;
try {
stats = await eventIndex.getStats();
stats = await eventIndex?.getStats();
} catch {
// This call may fail if sporadically, not a huge issue as we will
// try later again and probably succeed.
@ -126,8 +127,8 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
});
await EventIndexPeg.initEventIndex();
await EventIndexPeg.get().addInitialCheckpoints();
EventIndexPeg.get().startCrawler();
await EventIndexPeg.get()?.addInitialCheckpoints();
EventIndexPeg.get()?.startCrawler();
await SettingsStore.setValue("enableEventIndexing", null, SettingLevel.DEVICE, true);
await this.updateState();
};
@ -146,7 +147,7 @@ export default class EventIndexPanel extends React.Component<{}, IState> {
};
public render(): React.ReactNode {
let eventIndexingSettings = null;
let eventIndexingSettings: JSX.Element | undefined;
const brand = SdkConfig.get().brand;
if (EventIndexPeg.get() !== null) {

View file

@ -39,8 +39,8 @@ interface IState {
layout: Layout;
// User profile data for the message preview
userId?: string;
displayName: string;
avatarUrl: string;
displayName?: string;
avatarUrl?: string;
}
export default class FontScalingPanel extends React.Component<IProps, IState> {
@ -55,16 +55,13 @@ export default class FontScalingPanel extends React.Component<IProps, IState> {
fontSize: (SettingsStore.getValue("baseFontSize", null) + FontWatcher.SIZE_DIFF).toString(),
useCustomFontSize: SettingsStore.getValue("useCustomFontSize"),
layout: SettingsStore.getValue("layout"),
userId: null,
displayName: null,
avatarUrl: null,
};
}
public async componentDidMount(): Promise<void> {
// Fetch the current user profile for the message preview
const client = MatrixClientPeg.get();
const userId = client.getUserId();
const userId = client.getUserId()!;
const profileInfo = await client.getProfileInfo(userId);
if (this.unmounted) return;

View file

@ -74,14 +74,14 @@ const JoinRuleSettings: React.FC<IProps> = ({
? content.allow?.filter((o) => o.type === RestrictedAllowType.RoomMembership).map((o) => o.room_id)
: undefined;
const editRestrictedRoomIds = async (): Promise<string[] | undefined> => {
const editRestrictedRoomIds = async (): Promise<string[]> => {
let selected = restrictedAllowRoomIds;
if (!selected?.length && SpaceStore.instance.activeSpaceRoom) {
selected = [SpaceStore.instance.activeSpaceRoom.roomId];
}
const matrixClient = MatrixClientPeg.get();
const { finished } = Modal.createDialog(
const { finished } = Modal.createDialog<[string[]]>(
ManageRestrictedJoinRuleDialog,
{
matrixClient,
@ -127,7 +127,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
const shownSpaces = restrictedAllowRoomIds
.map((roomId) => cli.getRoom(roomId))
.filter((room) => room?.isSpaceRoom())
.slice(0, 4);
.slice(0, 4) as Room[];
let moreText;
if (shownSpaces.length < restrictedAllowRoomIds.length) {
@ -234,7 +234,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
const onChange = async (joinRule: JoinRule): Promise<void> => {
const beforeJoinRule = content.join_rule;
let restrictedAllowRoomIds: string[];
let restrictedAllowRoomIds: string[] | undefined;
if (joinRule === JoinRule.Restricted) {
if (beforeJoinRule === JoinRule.Restricted || roomSupportsRestricted) {
// Have the user pick which spaces to allow joins from
@ -244,8 +244,8 @@ const JoinRuleSettings: React.FC<IProps> = ({
// Block this action on a room upgrade otherwise it'd make their room unjoinable
const targetVersion = preferredRestrictionVersion;
let warning: JSX.Element;
const userId = cli.getUserId();
let warning: JSX.Element | undefined;
const userId = cli.getUserId()!;
const unableToUpdateSomeParents = Array.from(SpaceStore.instance.getKnownParents(room.roomId)).some(
(roomId) => !cli.getRoom(roomId)?.currentState.maySendStateEvent(EventType.SpaceChild, userId),
);
@ -332,7 +332,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
}
// when setting to 0 allowed rooms/spaces set to invite only instead as per the note
if (!restrictedAllowRoomIds.length) {
if (!restrictedAllowRoomIds?.length) {
joinRule = JoinRule.Invite;
}
}
@ -346,7 +346,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
// pre-set the accepted spaces with the currently viewed one as per the microcopy
if (joinRule === JoinRule.Restricted) {
newContent.allow = restrictedAllowRoomIds.map((roomId) => ({
newContent.allow = restrictedAllowRoomIds?.map((roomId) => ({
type: RestrictedAllowType.RoomMembership,
room_id: roomId,
}));

View file

@ -328,7 +328,7 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
this.setState({ phase: Phase.Persisting });
try {
const masterRule = this.state.masterPushRule;
const masterRule = this.state.masterPushRule!;
await MatrixClientPeg.get().setPushRuleEnabled("global", masterRule.kind, masterRule.rule_id, !checked);
await this.refreshFromServer();
} catch (e) {
@ -396,8 +396,8 @@ export default class Notifications extends React.PureComponent<IProps, IState> {
if (rule.ruleId === KEYWORD_RULE_ID) {
// Update all the keywords
for (const rule of this.state.vectorKeywordRuleInfo.rules) {
let enabled: boolean;
let actions: PushRuleAction[];
let enabled: boolean | undefined;
let actions: PushRuleAction[] | undefined;
if (checkedState === VectorState.On) {
if (rule.actions.length !== 1) {
// XXX: Magic number

View file

@ -33,14 +33,14 @@ import { accessSecretStorage } from "../../../SecurityManager";
interface IState {
loading: boolean;
error: null;
backupKeyStored: boolean;
backupKeyCached: boolean;
backupKeyWellFormed: boolean;
secretStorageKeyInAccount: boolean;
secretStorageReady: boolean;
backupInfo: IKeyBackupInfo;
backupSigStatus: TrustInfo;
error: Error | null;
backupKeyStored: boolean | null;
backupKeyCached: boolean | null;
backupKeyWellFormed: boolean | null;
secretStorageKeyInAccount: boolean | null;
secretStorageReady: boolean | null;
backupInfo: IKeyBackupInfo | null;
backupSigStatus: TrustInfo | null;
sessionsRemaining: number;
}
@ -144,10 +144,10 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
private async getUpdatedDiagnostics(): Promise<void> {
const cli = MatrixClientPeg.get();
const secretStorage = cli.crypto.secretStorage;
const secretStorage = cli.crypto!.secretStorage;
const backupKeyStored = !!(await cli.isKeyBackupKeyStored());
const backupKeyFromCache = await cli.crypto.getSessionBackupPrivateKey();
const backupKeyFromCache = await cli.crypto!.getSessionBackupPrivateKey();
const backupKeyCached = !!backupKeyFromCache;
const backupKeyWellFormed = backupKeyFromCache instanceof Uint8Array;
const secretStorageKeyInAccount = await secretStorage.hasKey();
@ -173,7 +173,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
this.loadBackupStatus();
},
},
null,
undefined,
/* priority = */ false,
/* static = */ true,
);
@ -200,7 +200,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
};
private restoreBackup = async (): Promise<void> => {
Modal.createDialog(RestoreKeyBackupDialog, null, null, /* priority = */ false, /* static = */ true);
Modal.createDialog(RestoreKeyBackupDialog, undefined, undefined, /* priority = */ false, /* static = */ true);
};
private resetSecretStorage = async (): Promise<void> => {
@ -233,7 +233,7 @@ export default class SecureBackupPanel extends React.PureComponent<{}, IState> {
let statusDescription;
let extraDetailsTableRows;
let extraDetails;
const actions = [];
const actions: JSX.Element[] = [];
if (error) {
statusDescription = <div className="error">{_t("Unable to load key backup status")}</div>;
} else if (loading) {

View file

@ -97,7 +97,6 @@ export default class SetIdServer extends React.Component<IProps, IState> {
defaultIdServer,
currentClientIdServer: MatrixClientPeg.get().getIdentityServerUrl(),
idServer: "",
error: null,
busy: false,
disconnectBusy: false,
checking: false,

View file

@ -133,7 +133,7 @@ interface IProps {
interface IState {
verifying: boolean;
addTask: AddThreepid;
addTask: AddThreepid | null;
continueDisabled: boolean;
newEmailAddress: string;
}
@ -201,7 +201,7 @@ export default class EmailAddresses extends React.Component<IProps, IState> {
this.setState({ continueDisabled: true });
this.state.addTask
.checkEmailLinkClicked()
?.checkEmailLinkClicked()
.then(([finished]) => {
let newEmailAddress = this.state.newEmailAddress;
if (finished) {

View file

@ -33,11 +33,11 @@ interface Props {
device?: ExtendedDevice;
isLoading: boolean;
isSigningOut: boolean;
localNotificationSettings?: LocalNotificationSettings | undefined;
localNotificationSettings?: LocalNotificationSettings;
// number of other sessions the user has
// excludes current session
otherSessionsCount: number;
setPushNotifications?: (deviceId: string, enabled: boolean) => Promise<void> | undefined;
setPushNotifications: (deviceId: string, enabled: boolean) => Promise<void>;
onVerifyCurrentDevice: () => void;
onSignOutCurrentDevice: () => void;
signOutAllOtherSessions?: () => void;

View file

@ -32,7 +32,7 @@ const makeDeleteRequest =
export const deleteDevicesWithInteractiveAuth = async (
matrixClient: MatrixClient,
deviceIds: string[],
onFinished?: InteractiveAuthCallback,
onFinished: InteractiveAuthCallback,
): Promise<void> => {
if (!deviceIds.length) {
return;

View file

@ -48,9 +48,9 @@ interface IEmailAddressProps {
interface IEmailAddressState {
verifying: boolean;
addTask: any; // FIXME: When AddThreepid is TSfied
addTask: AddThreepid | null;
continueDisabled: boolean;
bound: boolean;
bound?: boolean;
}
export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddressState> {
@ -172,7 +172,7 @@ export class EmailAddress extends React.Component<IEmailAddressProps, IEmailAddr
this.setState({ continueDisabled: true });
try {
await this.state.addTask.checkEmailLinkClicked();
await this.state.addTask?.checkEmailLinkClicked();
this.setState({
addTask: null,
continueDisabled: false,

View file

@ -41,10 +41,10 @@ interface IPhoneNumberProps {
interface IPhoneNumberState {
verifying: boolean;
verificationCode: string;
addTask: any; // FIXME: When AddThreepid is TSfied
addTask: AddThreepid | null;
continueDisabled: boolean;
bound: boolean;
verifyError: string;
bound?: boolean;
verifyError: string | null;
}
export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumberState> {
@ -89,6 +89,7 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
// a leading plus sign to a number in E.164 format (which the 3PID
// address is), but this goes against the spec.
// See https://github.com/matrix-org/matrix-doc/issues/2222
// @ts-ignore
await task.bindMsisdn(null, `+${address}`);
this.setState({
continueDisabled: false,
@ -128,8 +129,10 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
// address is), but this goes against the spec.
// See https://github.com/matrix-org/matrix-doc/issues/2222
if (bind) {
// @ts-ignore
await task.bindMsisdn(null, `+${address}`);
} else {
// @ts-ignore
await task.addMsisdn(null, `+${address}`);
}
this.setState({
@ -183,7 +186,7 @@ export class PhoneNumber extends React.Component<IPhoneNumberProps, IPhoneNumber
this.setState({ continueDisabled: true });
const token = this.state.verificationCode;
try {
await this.state.addTask.haveMsisdnToken(token);
await this.state.addTask?.haveMsisdnToken(token);
this.setState({
addTask: null,
continueDisabled: false,

View file

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { ReactNode } from "react";
import { SERVICE_TYPES } from "matrix-js-sdk/src/service-types";
import { IThreepid } from "matrix-js-sdk/src/@types/threepids";
import { logger } from "matrix-js-sdk/src/logger";
@ -64,20 +64,20 @@ interface IState {
spellCheckEnabled: boolean;
spellCheckLanguages: string[];
haveIdServer: boolean;
serverSupportsSeparateAddAndBind: boolean;
serverSupportsSeparateAddAndBind?: boolean;
idServerHasUnsignedTerms: boolean;
requiredPolicyInfo: {
// This object is passed along to a component for handling
hasTerms: boolean;
policiesAndServices: ServicePolicyPair[]; // From the startTermsFlow callback
agreedUrls: string[]; // From the startTermsFlow callback
resolve: (values: string[]) => void; // Promise resolve function for startTermsFlow callback
policiesAndServices: ServicePolicyPair[] | null; // From the startTermsFlow callback
agreedUrls: string[] | null; // From the startTermsFlow callback
resolve: ((values: string[]) => void) | null; // Promise resolve function for startTermsFlow callback
};
emails: IThreepid[];
msisdns: IThreepid[];
loading3pids: boolean; // whether or not the emails and msisdns have been loaded
canChangePassword: boolean;
idServerName: string;
idServerName?: string;
externalAccountManagementUrl?: string;
}
@ -92,7 +92,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
spellCheckEnabled: false,
spellCheckLanguages: [],
haveIdServer: Boolean(MatrixClientPeg.get().getIdentityServerUrl()),
serverSupportsSeparateAddAndBind: null,
idServerHasUnsignedTerms: false,
requiredPolicyInfo: {
// This object is passed along to a component for handling
@ -105,8 +104,6 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
msisdns: [],
loading3pids: true, // whether or not the emails and msisdns have been loaded
canChangePassword: false,
idServerName: null,
externalAccountManagementUrl: undefined,
};
this.dispatcherRef = dis.register(this.onAction);
@ -118,8 +115,8 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
public async componentDidMount(): Promise<void> {
const plat = PlatformPeg.get();
const [spellCheckEnabled, spellCheckLanguages] = await Promise.all([
plat.getSpellCheckEnabled(),
plat.getSpellCheckLanguages(),
plat?.getSpellCheckEnabled(),
plat?.getSpellCheckLanguages(),
]);
if (spellCheckLanguages) {
@ -301,7 +298,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
}
private renderAccountSection(): JSX.Element {
let passwordChangeForm = (
let passwordChangeForm: ReactNode = (
<ChangePassword
className="mx_GeneralUserSettingsTab_changePassword"
rowClassName=""
@ -311,7 +308,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
/>
);
let threepidSection = null;
let threepidSection: ReactNode = null;
// For older homeservers without separate 3PID add and bind methods (MSC2290),
// we use a combo add with bind option API which requires an identity server to
@ -345,7 +342,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
threepidSection = <Spinner />;
}
let passwordChangeText = _t("Set a new account password…");
let passwordChangeText: ReactNode = _t("Set a new account password…");
if (!this.state.canChangePassword) {
// Just don't show anything if you can't do anything.
passwordChangeText = null;
@ -483,7 +480,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
);
}
private renderIntegrationManagerSection(): JSX.Element {
private renderIntegrationManagerSection(): ReactNode {
if (!SettingsStore.getValue(UIFeature.Widgets)) return null;
return (
@ -496,7 +493,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
public render(): React.ReactNode {
const plaf = PlatformPeg.get();
const supportsMultiLanguageSpellCheck = plaf.supportsSpellCheckSettings();
const supportsMultiLanguageSpellCheck = plaf?.supportsSpellCheckSettings();
const discoWarning = this.state.requiredPolicyInfo.hasTerms ? (
<img
@ -508,7 +505,7 @@ export default class GeneralUserSettingsTab extends React.Component<IProps, ISta
/>
) : null;
let accountManagementSection;
let accountManagementSection: JSX.Element | undefined;
if (SettingsStore.getValue(UIFeature.Deactivate)) {
accountManagementSection = (
<>