From ba267f8fcd2a1b130643c53d5652eb5d15376166 Mon Sep 17 00:00:00 2001
From: Sorunome <mail@sorunome.de>
Date: Mon, 29 Oct 2018 20:53:05 +0100
Subject: [PATCH] strictNullChecks on main code

---
 src/bot.ts                  | 35 +++++++++++++++++++----------------
 src/channelsyncroniser.ts   | 30 +++++++++++++++---------------
 src/clientfactory.ts        |  6 +++---
 src/config.ts               |  4 ++--
 src/db/dbdataevent.ts       |  4 ++--
 src/db/schema/v3.ts         |  2 +-
 src/log.ts                  |  2 +-
 src/matrixeventprocessor.ts | 32 ++++++++++++++++++++------------
 src/matrixroomhandler.ts    |  6 +++---
 src/messageprocessor.ts     |  6 +++---
 src/presencehandler.ts      |  2 +-
 src/provisioner.ts          |  2 +-
 src/store.ts                |  2 +-
 src/usersyncroniser.ts      | 31 +++++++++++++++++--------------
 src/util.ts                 | 24 ++++++++++++------------
 tsconfig.json               |  3 ++-
 16 files changed, 103 insertions(+), 88 deletions(-)

diff --git a/src/bot.ts b/src/bot.ts
index d005e91..ccf0772 100644
--- a/src/bot.ts
+++ b/src/bot.ts
@@ -185,15 +185,15 @@ export class DiscordBot {
         }
         if (this.bot.guilds.has(guildId) ) {
             const guild = this.bot.guilds.get(guildId);
-            return guild.channels.filter((channel) => {
+            return guild!.channels.filter((channel) => {
                 return channel.name.toLowerCase() === channelName.toLowerCase(); // Implement searching in the future.
             }).map((channel) => {
                 return {
-                    alias: `#_discord_${guild.id}_${channel.id}:${this.config.bridge.domain}`,
+                    alias: `#_discord_${guild!.id}_${channel.id}:${this.config.bridge.domain}`,
                     fields: {
                         channel_id: channel.id,
                         channel_name: channel.name,
-                        guild_id: guild.id,
+                        guild_id: guild!.id,
                     },
                     protocol: "discord",
                 } as IThirdPartyLookup;
@@ -205,7 +205,7 @@ export class DiscordBot {
     }
 
     public async LookupRoom(server: string, room: string, sender?: string): Promise<ChannelLookupResult> {
-        const hasSender = sender !== null;
+        const hasSender = sender !== null && sender !== undefined;
         try {
             const client = await this.clientFactory.getClient(sender);
             const guild = client.guilds.get(server);
@@ -224,7 +224,7 @@ export class DiscordBot {
             log.verbose("LookupRoom => ", err);
             if (hasSender) {
                 log.verbose(`Couldn't find guild/channel under user account. Falling back.`);
-                return await this.LookupRoom(server, room, null);
+                return await this.LookupRoom(server, room);
             }
             throw err;
         }
@@ -253,7 +253,7 @@ export class DiscordBot {
         });
     }
 
-    public async ProcessMatrixMsgEvent(event: IMatrixEvent, guildId: string, channelId: string): Promise<null> {
+    public async ProcessMatrixMsgEvent(event: IMatrixEvent, guildId: string, channelId: string): Promise<void> {
         const mxClient = this.bridge.getClientFactory().getClientAs();
         log.verbose(`Looking up ${guildId}_${channelId}`);
         const result = await this.LookupRoom(guildId, channelId, event.sender);
@@ -277,8 +277,8 @@ export class DiscordBot {
             opts.file = file;
         }
 
-        let msg = null;
-        let hook: Discord.Webhook ;
+        let msg: Discord.Message | null | (Discord.Message | null)[] = null;
+        let hook: Discord.Webhook | undefined;
         if (botUser) {
             const webhooks = await chan.fetchWebhooks();
             hook = webhooks.filterArray((h) => h.name === "_matrix").pop();
@@ -300,14 +300,14 @@ export class DiscordBot {
                 msg = await chan.send(embed.description, opts);
             } else if (hook) {
                 msg = await hook.send(embed.description, {
-                    avatarURL: embed.author.icon_url,
+                    avatarURL: embed!.author!.icon_url,
                     embeds: embedSet.replyEmbed ? [embedSet.replyEmbed] : undefined,
                     files: opts.file ? [opts.file] : undefined,
-                    username: embed.author.name,
+                    username: embed!.author!.name,
                 } as Discord.WebhookMessageOptions);
             } else {
                 if (embedSet.replyEmbed) {
-                    embed.addField("Replying to", embedSet.replyEmbed.author.name);
+                    embed.addField("Replying to", embedSet.replyEmbed!.author!.name);
                     embed.addField("Reply text", embedSet.replyEmbed.description);
                 }
                 opts.embed = embed;
@@ -342,7 +342,7 @@ export class DiscordBot {
 
         const storeEvent = await this.store.Get(DbEvent, {matrix_id: event.redacts + ";" + event.room_id});
 
-        if (!storeEvent.Result) {
+        if (!storeEvent || !storeEvent.Result) {
             log.warn(`Could not redact because the event was not in the store.`);
             return;
         }
@@ -368,10 +368,10 @@ export class DiscordBot {
 
     public async GetDiscordUserOrMember(
         userId: Discord.Snowflake, guildId?: Discord.Snowflake,
-    ): Promise<Discord.User|Discord.GuildMember> {
+    ): Promise<Discord.User|Discord.GuildMember|undefined> {
         try {
             if (guildId && this.bot.guilds.has(guildId)) {
-                return await this.bot.guilds.get(guildId).fetchMember(userId);
+                return await this.bot.guilds.get(guildId)!.fetchMember(userId);
             }
             return await this.bot.fetchUser(userId);
         } catch (ex) {
@@ -405,7 +405,10 @@ export class DiscordBot {
         if (!id.match(/^\d+$/)) {
             throw new Error("Non-numerical ID");
         }
-        const dbEmoji: DbEmoji = await this.store.Get(DbEmoji, {emoji_id: id});
+        const dbEmoji = await this.store.Get(DbEmoji, {emoji_id: id});
+        if (!dbEmoji) {
+            throw new Error("Couldn't fetch from store");
+        }
         if (!dbEmoji.Result) {
             const url = "https://cdn.discordapp.com/emojis/" + id + (animated ? ".gif" : ".png");
             const intent = this.bridge.getIntent();
@@ -620,7 +623,7 @@ export class DiscordBot {
     private async DeleteDiscordMessage(msg: Discord.Message) {
         log.info(`Got delete event for ${msg.id}`);
         const storeEvent = await this.store.Get(DbEvent, {discord_id: msg.id});
-        if (!storeEvent.Result) {
+        if (!storeEvent || !storeEvent.Result) {
             log.warn(`Could not redact because the event was not in the store.`);
             return;
         }
diff --git a/src/channelsyncroniser.ts b/src/channelsyncroniser.ts
index 8cecd2e..997b72b 100644
--- a/src/channelsyncroniser.ts
+++ b/src/channelsyncroniser.ts
@@ -17,26 +17,26 @@ const DEFAULT_CHANNEL_STATE = {
 
 const DEFAULT_SINGLECHANNEL_STATE = {
     iconId: null,
-    iconUrl: null, // nullable
+    iconUrl: null,
     mxid: null,
-    name: null, // nullable
+    name: null,
     removeIcon: false,
-    topic: null, // nullable
+    topic: null,
 };
 
 export interface ISingleChannelState {
     mxid: string;
-    name: string; // nullable
-    topic: string; // nullable
-    iconUrl: string; // nullable
-    iconId: string; // nullable
+    name: string | null;
+    topic: string | null;
+    iconUrl: string | null;
+    iconId: string | null;
     removeIcon: boolean;
 }
 
 export interface IChannelState {
     id: string;
     mxChannels: ISingleChannelState[];
-    iconMxcUrl: string; // nullable
+    iconMxcUrl: string | null;
 }
 
 export class ChannelSyncroniser {
@@ -63,7 +63,7 @@ export class ChannelSyncroniser {
 
     public async OnGuildUpdate(guild: Discord.Guild, force = false) {
         log.verbose(`Got guild update for guild ${guild.id}`);
-        const channelStates = [];
+        const channelStates: IChannelState[] = [];
         for (const [_, channel] of guild.channels) {
             if (channel.type !== "text") {
                 continue; // not supported for now
@@ -76,7 +76,7 @@ export class ChannelSyncroniser {
             }
         }
 
-        let iconMxcUrl = null;
+        let iconMxcUrl: string | null = null;
         for (const channelState of channelStates) {
             channelState.iconMxcUrl = channelState.iconMxcUrl || iconMxcUrl;
             try {
@@ -135,7 +135,7 @@ export class ChannelSyncroniser {
 
     public async GetChannelUpdateState(channel: Discord.TextChannel, forceUpdate = false): Promise<IChannelState> {
         log.verbose(`State update request for ${channel.id}`);
-        const channelState = Object.assign({}, DEFAULT_CHANNEL_STATE, {
+        const channelState: IChannelState = Object.assign({}, DEFAULT_CHANNEL_STATE, {
             id: channel.id,
             mxChannels: [],
         });
@@ -150,19 +150,19 @@ export class ChannelSyncroniser {
             guild: channel.guild.name,
             name: "#" + channel.name,
         };
-        let name = this.config.channel.namePattern;
+        let name: string = this.config.channel.namePattern;
         for (const p of Object.keys(patternMap)) {
             name = name.replace(new RegExp(":" + p, "g"), patternMap[p]);
         }
         const topic = channel.topic;
         const icon = channel.guild.icon;
-        let iconUrl = null;
+        let iconUrl: string | null = null;
         if (icon) {
             iconUrl = `https://cdn.discordapp.com/icons/${channel.guild.id}/${icon}.png`;
         }
         remoteRooms.forEach((remoteRoom) => {
             const mxid = remoteRoom.matrix.getId();
-            const singleChannelState = Object.assign({}, DEFAULT_SINGLECHANNEL_STATE, {
+            const singleChannelState: ISingleChannelState = Object.assign({}, DEFAULT_SINGLECHANNEL_STATE, {
                 mxid,
             });
 
@@ -219,7 +219,7 @@ export class ChannelSyncroniser {
                 roomUpdated = true;
             }
 
-            if (channelState.iconUrl !== null) {
+            if (channelState.iconUrl !== null && channelState.iconId !== null) {
                 log.verbose(`Updating icon_url for ${channelState.mxid} to "${channelState.iconUrl}"`);
                 if (channelsState.iconMxcUrl === null) {
                     const iconMxc = await Util.UploadContentFromUrl(
diff --git a/src/clientfactory.ts b/src/clientfactory.ts
index adb3bb8..3f5b947 100644
--- a/src/clientfactory.ts
+++ b/src/clientfactory.ts
@@ -15,7 +15,7 @@ export class DiscordClientFactory {
     private botClient: Matrix.Client;
     private clients: Map<string, Matrix.Client>;
     constructor(store: DiscordStore, config?: DiscordBridgeConfigAuth) {
-        this.config = config;
+        this.config = config!;
         this.clients = new Map();
         this.store = store;
     }
@@ -59,8 +59,8 @@ export class DiscordClientFactory {
         });
     }
 
-    public async getClient(userId: string = null): Promise<Matrix.Client> {
-        if (userId == null) {
+    public async getClient(userId: string | null = null): Promise<Matrix.Client> {
+        if (userId === null) {
             return this.botClient;
         }
         if (this.clients.has(userId)) {
diff --git a/src/config.ts b/src/config.ts
index 031a7ee..c5dfb88 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -67,8 +67,8 @@ class DiscordBridgeConfigChannel {
 }
 
 class DiscordBridgeConfigChannelDeleteOptions {
-    public namePrefix: string = null;
-    public topicPrefix: string = null;
+    public namePrefix: string | null = null;
+    public topicPrefix: string | null = null;
     public disableMessaging: boolean = false;
     public unsetRoomAlias: boolean = true;
     public unlistFromDirectory: boolean = true;
diff --git a/src/db/dbdataevent.ts b/src/db/dbdataevent.ts
index 29fd9ec..496a428 100644
--- a/src/db/dbdataevent.ts
+++ b/src/db/dbdataevent.ts
@@ -17,7 +17,8 @@ export class DbEvent implements IDbDataMany {
     // tslint:disable-next-line no-any
     public async RunQuery(store: DiscordStore, params: any): Promise<void> {
         this.rows = [];
-        let rowsM = null;
+        // tslint:disable-next-line no-any
+        let rowsM: any[] | null = null;
         if (params.matrix_id) {
             rowsM = await store.db.All(`
                 SELECT *
@@ -55,7 +56,6 @@ export class DbEvent implements IDbDataMany {
             }
         }
         this.Result = this.rows.length !== 0;
-        return null;
     }
 
     public Next(): boolean {
diff --git a/src/db/schema/v3.ts b/src/db/schema/v3.ts
index 952e046..60cf3e7 100644
--- a/src/db/schema/v3.ts
+++ b/src/db/schema/v3.ts
@@ -42,7 +42,7 @@ export class Schema implements IDbSchema {
         )]);
     }
 
-    private async moveUserIds(store: DiscordStore): Promise <null> {
+    private async moveUserIds(store: DiscordStore): Promise <void> {
         log.info("Performing one time moving of tokens to new table. Please wait.");
         let rows;
         try {
diff --git a/src/log.ts b/src/log.ts
index 8a895de..96905f9 100644
--- a/src/log.ts
+++ b/src/log.ts
@@ -29,7 +29,7 @@ export class Log {
     }
 
     private static config: DiscordBridgeConfigLogging;
-    private static logger: Logger = null;
+    private static logger: Logger;
 
     private static now() {
         return moment().format(Log.config.lineDateFormat);
diff --git a/src/matrixeventprocessor.ts b/src/matrixeventprocessor.ts
index 8991490..7cbe8d0 100644
--- a/src/matrixeventprocessor.ts
+++ b/src/matrixeventprocessor.ts
@@ -45,7 +45,7 @@ export class MatrixEventProcessor {
         this.discord = opts.discord;
     }
 
-    public StateEventToMessage(event: IMatrixEvent, channel: Discord.TextChannel): string {
+    public StateEventToMessage(event: IMatrixEvent, channel: Discord.TextChannel): string | undefined {
         const SUPPORTED_EVENTS = ["m.room.member", "m.room.name", "m.room.topic"];
         if (!SUPPORTED_EVENTS.includes(event.type)) {
             log.verbose(`${event.event_id} ${event.type} is not displayable.`);
@@ -60,11 +60,11 @@ export class MatrixEventProcessor {
         let msg = `\`${event.sender}\` `;
 
         if (event.type === "m.room.name") {
-            msg += `set the name to \`${event.content.name}\``;
+            msg += `set the name to \`${event.content!.name}\``;
         } else if (event.type === "m.room.topic") {
-            msg += `set the topic to \`${event.content.topic}\``;
+            msg += `set the topic to \`${event.content!.topic}\``;
         } else if (event.type === "m.room.member") {
-            const membership = event.content.membership;
+            const membership = event.content!.membership;
             if (membership === "join"
                 && event.unsigned.prev_content === undefined) {
                 msg += `joined the room`;
@@ -86,9 +86,9 @@ export class MatrixEventProcessor {
     public async EventToEmbed(
         event: IMatrixEvent, profile: IMatrixEvent|null, channel: Discord.TextChannel,
     ): Promise<IMatrixEventProcessorResult> {
-        let body = this.config.bridge.disableDiscordMentions ? event.content.body :
+        let body: string = this.config.bridge.disableDiscordMentions ? event.content!.body as string :
             this.FindMentionsInPlainBody(
-                event.content.body,
+                event.content!.body as string,
                 channel.members.array(),
             );
 
@@ -113,7 +113,7 @@ export class MatrixEventProcessor {
         }*/
 
         // Replace /me with * username ...
-        if (event.content.msgtype === "m.emote") {
+        if (event.content!.msgtype === "m.emote") {
             if (profile &&
                 profile.displayname &&
                 profile.displayname.length >= MIN_NAME_LENGTH &&
@@ -172,26 +172,30 @@ export class MatrixEventProcessor {
     }
 
     public async HandleAttachment(event: IMatrixEvent, mxClient: Matrix.Client): Promise<string|Discord.FileOptions> {
+        if (!event.content) {
+            event.content = {};
+        }
+
         const hasAttachment = [
             "m.image",
             "m.audio",
             "m.video",
             "m.file",
             "m.sticker",
-        ].includes(event.content.msgtype) || [
+        ].includes(event.content.msgtype as string) || [
             "m.sticker",
         ].includes(event.type);
         if (!hasAttachment) {
             return "";
         }
 
-        if (event.content.info == null) {
+        if (!event.content.info) {
             // Fractal sends images without an info, which is technically allowed
             // but super unhelpful:  https://gitlab.gnome.org/World/fractal/issues/206
             event.content.info = {size: 0};
         }
 
-        if (event.content.url == null) {
+        if (!event.content.url) {
             log.info("Event was an attachment type but was missing a content.url");
             return "";
         }
@@ -213,6 +217,10 @@ export class MatrixEventProcessor {
     }
 
     public async GetEmbedForReply(event: IMatrixEvent): Promise<[Discord.RichEmbed, string]|undefined> {
+        if (!event.content) {
+            event.content = {};
+        }
+
         const relatesTo = event.content["m.relates_to"];
         let eventId = null;
         if (relatesTo && relatesTo["m.in_reply_to"]) {
@@ -250,7 +258,7 @@ export class MatrixEventProcessor {
         return [embed, reponseText];
     }
 
-    private async SetEmbedAuthor(embed: Discord.RichEmbed, sender: string, profile?: IMatrixEvent) {
+    private async SetEmbedAuthor(embed: Discord.RichEmbed, sender: string, profile?: IMatrixEvent | null) {
         const intent = this.bridge.getIntent();
         let displayName = sender;
         let avatarUrl;
@@ -274,7 +282,7 @@ export class MatrixEventProcessor {
             }
             // Let it fall through.
         }
-        if (profile === undefined) {
+        if (!profile) {
             try {
                 profile = await intent.getProfileInfo(sender);
             } catch (ex) {
diff --git a/src/matrixroomhandler.ts b/src/matrixroomhandler.ts
index 265cba1..7c107d3 100644
--- a/src/matrixroomhandler.ts
+++ b/src/matrixroomhandler.ts
@@ -81,7 +81,7 @@ export class MatrixRoomHandler {
             "public",
         );
         await this.discord.ChannelSyncroniser.OnUpdate(channel);
-        const promiseList = [];
+        const promiseList: Promise<void>[] = [];
         /* We delay the joins to give some implementations a chance to breathe */
         // Join a whole bunch of users.
         /* We delay the joins to give some implementations a chance to breathe */
@@ -228,7 +228,7 @@ export class MatrixRoomHandler {
             });
         }
 
-        const {command, args} = Util.MsgToArgs(event.content.body, "!discord");
+        const {command, args} = Util.MsgToArgs(event.content!.body as string, "!discord");
 
         if (command === "help" && args[0] === "bridge") {
             const link = Util.GetBotLink(this.config);
@@ -505,7 +505,7 @@ export class MatrixRoomHandler {
 
     private DiscordModerationActionGenerator(discordChannel: Discord.TextChannel, funcKey: string, action: string) {
         return async ({name}) => {
-            let allChannelMxids = [];
+            let allChannelMxids: string[] = [];
             await Promise.all(discordChannel.guild.channels.map(async (chan) => {
                 try {
                     const chanMxids = await this.discord.ChannelSyncroniser.GetRoomIdsFromChannel(chan);
diff --git a/src/messageprocessor.ts b/src/messageprocessor.ts
index 32beb2f..5f4967a 100644
--- a/src/messageprocessor.ts
+++ b/src/messageprocessor.ts
@@ -45,7 +45,7 @@ function _setupMarked() {
 }
 
 export class MessageProcessorOpts {
-    constructor(readonly domain: string, readonly bot: DiscordBot = null) {
+    constructor(readonly domain: string, readonly bot: DiscordBot) {
 
     }
 }
@@ -57,9 +57,9 @@ export class MessageProcessorMatrixResult {
 
 export class MessageProcessor {
     private readonly opts: MessageProcessorOpts;
-    constructor(opts: MessageProcessorOpts, bot: DiscordBot = null) {
+    constructor(opts: MessageProcessorOpts, bot: DiscordBot | null = null) {
         // Backwards compat
-        if (bot != null) {
+        if (bot !== null) {
             this.opts = new MessageProcessorOpts(opts.domain, bot);
         } else {
             this.opts = opts;
diff --git a/src/presencehandler.ts b/src/presencehandler.ts
index 9c6d946..eb661c5 100644
--- a/src/presencehandler.ts
+++ b/src/presencehandler.ts
@@ -44,7 +44,7 @@ export class PresenceHandler {
         }
         log.info("Stopping presence handler");
         clearInterval(this.interval);
-        this.interval = null;
+        this.interval = 0;
     }
 
     public EnqueueUser(user: User) {
diff --git a/src/provisioner.ts b/src/provisioner.ts
index 1a43db0..044e5a5 100644
--- a/src/provisioner.ts
+++ b/src/provisioner.ts
@@ -77,7 +77,7 @@ export class Provisioner {
         }
 
         const perms = channel.permissionsFor(member);
-        if (!perms.hasPermission(Discord.Permissions.FLAGS.MANAGE_WEBHOOKS)) {
+        if (!perms.hasPermission(Discord.Permissions.FLAGS.MANAGE_WEBHOOKS as Discord.PermissionResolvable)) {
             // Missing permissions, so just reject it
             throw new Error("You do not have permission to manage webhooks in this channel");
         }
diff --git a/src/store.ts b/src/store.ts
index 0b0ef70..060750f 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -26,7 +26,7 @@ export class DiscordStore {
         } else {
             this.config = configOrFile;
         }
-        this.version = null;
+        this.version = 0;
     }
 
     public async backup_database(): Promise<void|{}> {
diff --git a/src/usersyncroniser.ts b/src/usersyncroniser.ts
index 89ff6ab..6bf6862 100644
--- a/src/usersyncroniser.ts
+++ b/src/usersyncroniser.ts
@@ -9,17 +9,17 @@ import { Log } from "./log";
 const log = new Log("UserSync");
 
 const DEFAULT_USER_STATE = {
-    avatarId: null,
-    avatarUrl: null, // Nullable
+    avatarId: "",
+    avatarUrl: null,
     createUser: false,
-    displayName: null, // Nullable
+    displayName: null,
     id: null,
     mxUserId: null,
     removeAvatar: false,
 };
 
 const DEFAULT_GUILD_STATE = {
-    displayName: null,
+    displayName: "",
     id: null,
     mxUserId: null,
     roles: [],
@@ -27,9 +27,9 @@ const DEFAULT_GUILD_STATE = {
 
 export interface IUserState {
     avatarId: string;
-    avatarUrl: string; // Nullable
+    avatarUrl: string | null;
     createUser: boolean;
-    displayName: string; // Nullable
+    displayName: string | null;
     id: string;
     mxUserId: string;
     removeAvatar: boolean; // If the avatar has been removed from the user.
@@ -88,7 +88,7 @@ export class UserSyncroniser {
     public async ApplyStateToProfile(userState: IUserState) {
         const intent = this.bridge.getIntent(userState.mxUserId);
         let userUpdated = false;
-        let remoteUser = null;
+        let remoteUser: RemoteUser;
         if (userState.createUser) {
             /* NOTE: Setting the displayname/avatar will register the user if they don't exist */
             log.info(`Creating new user ${userState.mxUserId}`);
@@ -184,7 +184,7 @@ export class UserSyncroniser {
 
     public async GetUserUpdateState(discordUser: User): Promise<IUserState> {
         log.verbose(`State update requested for ${discordUser.id}`);
-        const userState = Object.assign({}, DEFAULT_USER_STATE, {
+        const userState: IUserState = Object.assign({}, DEFAULT_USER_STATE, {
             id: discordUser.id,
             mxUserId: `@_discord_${discordUser.id}:${this.config.bridge.domain}`,
         });
@@ -222,9 +222,12 @@ export class UserSyncroniser {
 
     public async GetUserStateForGuildMember(
         newMember: GuildMember,
-        displayname: string,
+        displayname?: string,
     ): Promise<IGuildMemberState> {
-        const guildState = Object.assign({}, DEFAULT_GUILD_STATE, {
+        if (!displayname) {
+            displayname = "";
+        }
+        const guildState: IGuildMemberState = Object.assign({}, DEFAULT_GUILD_STATE, {
             id: newMember.id,
             mxUserId: `@_discord_${newMember.id}:${this.config.bridge.domain}`,
             roles: newMember.roles.map((role) => { return {
@@ -284,7 +287,7 @@ export class UserSyncroniser {
             if (guild.members.has(id)) {
                 log.info(`Updating user ${id} in guild ${guild.id}.`);
                 const member = guild.members.get(id);
-                const state = await this.GetUserStateForGuildMember(member, remoteUser.get("displayname"));
+                const state = await this.GetUserStateForGuildMember(member!, remoteUser.get("displayname"));
                 const rooms = await this.discord.GetRoomIdsFromGuild(guild.id);
                 return Promise.all(
                     rooms.map(
@@ -324,7 +327,7 @@ export class UserSyncroniser {
             log.warn(`Got member update for ${roomId}, but no channel or guild member could be found.`);
             return UserSyncroniser.ERR_CHANNEL_MEMBER_NOT_FOUND;
         }
-        const state = await this.GetUserStateForGuildMember(member, ev.content.displayname);
+        const state = await this.GetUserStateForGuildMember(member, ev.content!.displayname);
         return this.ApplyStateToRoom(state, roomId, member.guild.id);
     }
 
@@ -332,14 +335,14 @@ export class UserSyncroniser {
         const userStateKey = `${ev.room_id}${ev.state_key}`;
         if (this.userStateHold.has(userStateKey)) {
             const oldEv = this.userStateHold.get(userStateKey);
-            if (ev.origin_server_ts > oldEv.origin_server_ts) {
+            if (ev.origin_server_ts! > oldEv!.origin_server_ts!) {
                 return false; // New event is older
             }
         }
         this.userStateHold.set(userStateKey, ev);
         // tslint:disable-next-line:await-promise
         await Bluebird.delay(delayMs);
-        if (this.userStateHold.get(userStateKey).event_id !== ev.event_id) {
+        if (this.userStateHold.get(userStateKey)!.event_id !== ev.event_id) {
             // Event has changed and we are out of date.
             return false;
         }
diff --git a/src/util.ts b/src/util.ts
index 09afd90..10ea21a 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -94,7 +94,7 @@ export class Util {
      * uploadContentFromUrl - Upload content from a given URL to the homeserver
      * and return a MXC URL.
      */
-    public static async UploadContentFromUrl(url: string, intent: Intent, name: string): Promise<IUploadResult> {
+    public static async UploadContentFromUrl(url: string, intent: Intent, name: string | null): Promise<IUploadResult> {
         let contenttype;
         let size;
         name = name || null;
@@ -164,16 +164,16 @@ export class Util {
 
     public static GetBotLink(config: DiscordBridgeConfig): string {
         /* tslint:disable:no-bitwise */
-        const perms = Permissions.FLAGS.READ_MESSAGES |
-            Permissions.FLAGS.SEND_MESSAGES |
-            Permissions.FLAGS.CHANGE_NICKNAME |
-            Permissions.FLAGS.CONNECT |
-            Permissions.FLAGS.SPEAK |
-            Permissions.FLAGS.EMBED_LINKS |
-            Permissions.FLAGS.ATTACH_FILES |
-            Permissions.FLAGS.READ_MESSAGE_HISTORY |
-            Permissions.FLAGS.MANAGE_WEBHOOKS |
-            Permissions.FLAGS.MANAGE_MESSAGES;
+        const perms = Permissions.FLAGS.READ_MESSAGES! |
+            Permissions.FLAGS.SEND_MESSAGES! |
+            Permissions.FLAGS.CHANGE_NICKNAME! |
+            Permissions.FLAGS.CONNECT! |
+            Permissions.FLAGS.SPEAK! |
+            Permissions.FLAGS.EMBED_LINKS! |
+            Permissions.FLAGS.ATTACH_FILES! |
+            Permissions.FLAGS.READ_MESSAGE_HISTORY! |
+            Permissions.FLAGS.MANAGE_WEBHOOKS! |
+            Permissions.FLAGS.MANAGE_MESSAGES!;
         /* tslint:enable:no-bitwise */
 
         const clientId = config.auth.clientID;
@@ -254,7 +254,7 @@ export class Util {
     public static MsgToArgs(msg: string, prefix: string) {
         prefix += " ";
         let command = "help";
-        let args = [];
+        let args: string[] = [];
         if (msg.length >= prefix.length) {
             const allArgs = msg.substring(prefix.length).split(" ");
             if (allArgs.length && allArgs[0] !== "") {
diff --git a/tsconfig.json b/tsconfig.json
index acfe5f9..5b8c855 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,8 @@
         "noImplicitAny": false,
         "sourceMap": false,
         "outDir": "./build",
-        "types": ["mocha", "node"]
+        "types": ["mocha", "node"],
+        "strictNullChecks": true
     },
     "compileOnSave": true,
     "include": [
-- 
GitLab