diff --git a/packages/desktop-client/src/components/UpdateNotification.tsx b/packages/desktop-client/src/components/UpdateNotification.tsx
index 1485a56a96b399559838f0ad265e16edc8e4910d..58b769d4596d78b223f159653d4cf535f902b2f1 100644
--- a/packages/desktop-client/src/components/UpdateNotification.tsx
+++ b/packages/desktop-client/src/components/UpdateNotification.tsx
@@ -1,4 +1,3 @@
-// @ts-strict-ignore
 import React from 'react';
 import { useSelector } from 'react-redux';
 
@@ -11,14 +10,6 @@ import { LinkButton } from './common/LinkButton';
 import { Text } from './common/Text';
 import { View } from './common/View';
 
-function closeNotification(setAppState) {
-  // Set a flag to never show an update notification again for this session
-  setAppState({
-    updateInfo: null,
-    showUpdateNotification: false,
-  });
-}
-
 export function UpdateNotification() {
   const updateInfo = useSelector(state => state.app.updateInfo);
   const showUpdateNotification = useSelector(
@@ -68,7 +59,7 @@ export function UpdateNotification() {
                   textDecoration: 'underline',
                 }}
                 onClick={() =>
-                  window.Actual.openURLInBrowser(
+                  window.Actual?.openURLInBrowser(
                     'https://actualbudget.org/docs/releases',
                   )
                 }
@@ -80,7 +71,13 @@ export function UpdateNotification() {
                 type="bare"
                 aria-label="Close"
                 style={{ display: 'inline', padding: '1px 7px 2px 7px' }}
-                onClick={() => closeNotification(setAppState)}
+                onClick={() => {
+                  // Set a flag to never show an update notification again for this session
+                  setAppState({
+                    updateInfo: null,
+                    showUpdateNotification: false,
+                  });
+                }}
               >
                 <SvgClose
                   width={9}
diff --git a/packages/desktop-client/src/components/autocomplete/Autocomplete.tsx b/packages/desktop-client/src/components/autocomplete/Autocomplete.tsx
index f7079b57c6229beeed19a99b5816aa0645b11533..9938b6f051e60378e580d183c1fbfc9d8e90de04 100644
--- a/packages/desktop-client/src/components/autocomplete/Autocomplete.tsx
+++ b/packages/desktop-client/src/components/autocomplete/Autocomplete.tsx
@@ -691,7 +691,7 @@ function MultiAutocomplete<T extends Item>({
 
 type AutocompleteFooterProps = {
   show?: boolean;
-  embedded: boolean;
+  embedded?: boolean;
   children: ReactNode;
 };
 export function AutocompleteFooter({
@@ -699,18 +699,20 @@ export function AutocompleteFooter({
   embedded,
   children,
 }: AutocompleteFooterProps) {
+  if (!show) {
+    return null;
+  }
+
   return (
-    show && (
-      <View
-        style={{
-          flexShrink: 0,
-          ...(embedded ? { paddingTop: 5 } : { padding: 5 }),
-        }}
-        onMouseDown={e => e.preventDefault()}
-      >
-        {children}
-      </View>
-    )
+    <View
+      style={{
+        flexShrink: 0,
+        ...(embedded ? { paddingTop: 5 } : { padding: 5 }),
+      }}
+      onMouseDown={e => e.preventDefault()}
+    >
+      {children}
+    </View>
   );
 }
 
diff --git a/packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx b/packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx
index 2fca26a7a7120a67f8d9eff646a5119723439597..bb34a8eeaa4e9ae180b27aa87d8235495419b6de 100644
--- a/packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx
+++ b/packages/desktop-client/src/components/autocomplete/CategoryAutocomplete.tsx
@@ -1,4 +1,3 @@
-// @ts-strict-ignore
 import React, {
   type ComponentProps,
   Fragment,
@@ -25,9 +24,11 @@ import { Autocomplete, defaultFilterSuggestion } from './Autocomplete';
 
 export type CategoryListProps = {
   items: Array<CategoryEntity & { group?: CategoryGroupEntity }>;
-  getItemProps?: (arg: { item }) => Partial<ComponentProps<typeof View>>;
+  getItemProps?: (arg: {
+    item: CategoryEntity;
+  }) => Partial<ComponentProps<typeof View>>;
   highlightedIndex: number;
-  embedded: boolean;
+  embedded?: boolean;
   footer?: ReactNode;
   renderSplitTransactionButton?: (
     props: SplitTransactionButtonProps,
@@ -47,7 +48,7 @@ function CategoryList({
   renderCategoryItemGroupHeader = defaultRenderCategoryItemGroupHeader,
   renderCategoryItem = defaultRenderCategoryItem,
 }: CategoryListProps) {
-  let lastGroup = null;
+  let lastGroup: string | undefined | null = null;
 
   return (
     <View>
@@ -72,10 +73,10 @@ function CategoryList({
           lastGroup = item.cat_group;
           return (
             <Fragment key={item.id}>
-              {showGroup && (
-                <Fragment key={item.group?.name}>
+              {showGroup && item.group?.name && (
+                <Fragment key={item.group.name}>
                   {renderCategoryItemGroupHeader({
-                    title: item.group?.name,
+                    title: item.group.name,
                   })}
                 </Fragment>
               )}
@@ -125,7 +126,7 @@ export function CategoryAutocomplete({
       categoryGroups.reduce(
         (list, group) =>
           list.concat(
-            group.categories
+            (group.categories || [])
               .filter(category => category.cat_group === group.id)
               .map(category => ({
                 ...category,
@@ -214,8 +215,7 @@ type SplitTransactionButtonProps = {
   style?: CSSProperties;
 };
 
-// eslint-disable-next-line import/no-unused-modules
-export function SplitTransactionButton({
+function SplitTransactionButton({
   Icon,
   highlighted,
   embedded,
diff --git a/packages/loot-core/src/platform/server/connection/index.electron.ts b/packages/loot-core/src/platform/server/connection/index.electron.ts
index 75e6a273106aad8c90201d06819b213e807b1f89..5e48c200d37364a5906d63c827fe52bb7556d9ba 100644
--- a/packages/loot-core/src/platform/server/connection/index.electron.ts
+++ b/packages/loot-core/src/platform/server/connection/index.electron.ts
@@ -1,4 +1,5 @@
 // @ts-strict-ignore
+import { APIError } from '../../../server/errors';
 import { runHandler, isMutating } from '../../../server/mutators';
 import { captureException } from '../../exceptions';
 
@@ -70,7 +71,7 @@ export const init: T.Init = function (_socketName, handlers) {
         type: 'reply',
         id,
         result: null,
-        error: { type: 'APIError', message: 'Unknown method: ' + name },
+        error: APIError('Unknown method: ' + name),
       });
     }
   });
diff --git a/packages/loot-core/src/platform/server/connection/index.web.ts b/packages/loot-core/src/platform/server/connection/index.web.ts
index 212f64e96eb462f3e2d0c5c7c8008cb7795a3ac5..465b8348edcbf7631ddcd5f97ce60b0fef1a6dc7 100644
--- a/packages/loot-core/src/platform/server/connection/index.web.ts
+++ b/packages/loot-core/src/platform/server/connection/index.web.ts
@@ -1,4 +1,5 @@
 // @ts-strict-ignore
+import { APIError } from '../../../server/errors';
 import { runHandler, isMutating } from '../../../server/mutators';
 import { captureException } from '../../exceptions';
 
@@ -90,7 +91,7 @@ export const init: T.Init = function (serverChn, handlers) {
           type: 'reply',
           id,
           result: null,
-          error: { type: 'APIError', message: 'Unknown method: ' + name },
+          error: APIError('Unknown method: ' + name),
         });
       }
     },
diff --git a/packages/loot-core/src/server/api.ts b/packages/loot-core/src/server/api.ts
index a89625971e9c82a3462a5ac6ce98d438bc7223cc..2956ce61aefed3e061a5d95445057b4453881cb2 100644
--- a/packages/loot-core/src/server/api.ts
+++ b/packages/loot-core/src/server/api.ts
@@ -28,6 +28,7 @@ import {
 import { runQuery as aqlQuery } from './aql';
 import * as cloudStorage from './cloud-storage';
 import * as db from './db';
+import { APIError } from './errors';
 import { runMutator } from './mutators';
 import * as prefs from './prefs';
 import * as sheet from './sheet';
@@ -35,11 +36,6 @@ import { setSyncingMode, batchMessages } from './sync';
 
 let IMPORT_MODE = false;
 
-// This is duplicate from main.js...
-function APIError(msg, meta?) {
-  return { type: 'APIError', message: msg, meta };
-}
-
 // The API is different in two ways: we never want undo enabled, and
 // we also need to notify the UI manually if stuff has changed (if
 // they are connecting to an already running instance, the UI should
diff --git a/packages/loot-core/src/server/db/sort.ts b/packages/loot-core/src/server/db/sort.ts
index 1a8b96378e1f9bb62d368da3ea54c589c1e2e662..eb59865317d0d1f16e518d26e465339ef809bde3 100644
--- a/packages/loot-core/src/server/db/sort.ts
+++ b/packages/loot-core/src/server/db/sort.ts
@@ -1,7 +1,6 @@
-// @ts-strict-ignore
 export const SORT_INCREMENT = 16384;
 
-function midpoint(items, to) {
+function midpoint<T extends { sort_order: number }>(items: T[], to: number) {
   const below = items[to - 1];
   const above = items[to];
 
@@ -14,11 +13,14 @@ function midpoint(items, to) {
   }
 }
 
-export function shoveSortOrders(items, targetId?: string) {
+export function shoveSortOrders<T extends { id: string; sort_order: number }>(
+  items: T[],
+  targetId?: string,
+) {
   const to = items.findIndex(item => item.id === targetId);
   const target = items[to];
   const before = items[to - 1];
-  const updates = [];
+  const updates: Array<{ id: string; sort_order: number }> = [];
 
   // If no target is specified, append at the end
   if (!targetId || to === -1) {
diff --git a/packages/loot-core/src/server/errors.ts b/packages/loot-core/src/server/errors.ts
index 96606b4be2fa71cdc41c6365134b37a0b1f9b225..1edf89a9b6838d8ccec547e41f07c274446b6706 100644
--- a/packages/loot-core/src/server/errors.ts
+++ b/packages/loot-core/src/server/errors.ts
@@ -1,11 +1,10 @@
-// @ts-strict-ignore
 // TODO: normalize error types
 export class PostError extends Error {
-  meta;
-  reason;
-  type;
+  meta?: { meta: string };
+  reason: string;
+  type: 'PostError';
 
-  constructor(reason, meta?) {
+  constructor(reason: string, meta?: { meta: string }) {
     super('PostError: ' + reason);
     this.type = 'PostError';
     this.reason = reason;
@@ -14,10 +13,10 @@ export class PostError extends Error {
 }
 
 export class HTTPError extends Error {
-  statusCode;
-  responseBody;
+  statusCode: number;
+  responseBody: string;
 
-  constructor(code, body) {
+  constructor(code: number, body: string) {
     super(`HTTPError: unsuccessful status code (${code}): ${body}`);
     this.statusCode = code;
     this.responseBody = body;
@@ -25,10 +24,27 @@ export class HTTPError extends Error {
 }
 
 export class SyncError extends Error {
-  meta;
-  reason;
+  meta?:
+    | {
+        isMissingKey: boolean;
+      }
+    | {
+        error: { message: string; stack: string };
+        query: { sql: string; params: Array<string | number> };
+      };
+  reason: string;
 
-  constructor(reason, meta?) {
+  constructor(
+    reason: string,
+    meta?:
+      | {
+          isMissingKey: boolean;
+        }
+      | {
+          error: { message: string; stack: string };
+          query: { sql: string; params: Array<string | number> };
+        },
+  ) {
     super('SyncError: ' + reason);
     this.reason = reason;
     this.meta = meta;
@@ -46,14 +62,20 @@ export class RuleError extends Error {
   }
 }
 
-export function APIError(msg, meta?) {
-  return { type: 'APIError', message: msg, meta };
+export function APIError(msg: string) {
+  return { type: 'APIError', message: msg };
 }
 
-export function FileDownloadError(reason, meta?) {
+export function FileDownloadError(
+  reason: string,
+  meta?: { fileId?: string; isMissingKey?: boolean },
+) {
   return { type: 'FileDownloadError', reason, meta };
 }
 
-export function FileUploadError(reason, meta?) {
+export function FileUploadError(
+  reason: string,
+  meta?: { isMissingKey: boolean },
+) {
   return { type: 'FileUploadError', reason, meta };
 }
diff --git a/packages/loot-core/src/server/models.ts b/packages/loot-core/src/server/models.ts
index 3616d7cf92ce9671df3442a3776e310f863d57ca..03ddbeab06063e5058faf2f8f8ce5abe6b1f1314 100644
--- a/packages/loot-core/src/server/models.ts
+++ b/packages/loot-core/src/server/models.ts
@@ -1,19 +1,30 @@
-// @ts-strict-ignore
-export function requiredFields(name, row, fields, update) {
+import {
+  AccountEntity,
+  CategoryEntity,
+  CategoryGroupEntity,
+  PayeeEntity,
+} from '../types/models';
+
+export function requiredFields<T extends object, K extends keyof T>(
+  name: string,
+  row: T,
+  fields: K[],
+  update?: boolean,
+) {
   fields.forEach(field => {
     if (update) {
       if (row.hasOwnProperty(field) && row[field] == null) {
-        throw new Error(`${name} is missing field ${field}`);
+        throw new Error(`${name} is missing field ${String(field)}`);
       }
     } else {
       if (!row.hasOwnProperty(field) || row[field] == null) {
-        throw new Error(`${name} is missing field ${field}`);
+        throw new Error(`${name} is missing field ${String(field)}`);
       }
     }
   });
 }
 
-export function toDateRepr(str) {
+export function toDateRepr(str: string) {
   if (typeof str !== 'string') {
     throw new Error('toDateRepr not passed a string: ' + str);
   }
@@ -21,7 +32,7 @@ export function toDateRepr(str) {
   return parseInt(str.replace(/-/g, ''));
 }
 
-export function fromDateRepr(number) {
+export function fromDateRepr(number: number) {
   if (typeof number !== 'number') {
     throw new Error('fromDateRepr not passed a number: ' + number);
   }
@@ -37,7 +48,7 @@ export function fromDateRepr(number) {
 }
 
 export const accountModel = {
-  validate(account, { update }: { update?: boolean } = {}) {
+  validate(account: AccountEntity, { update }: { update?: boolean } = {}) {
     requiredFields(
       'account',
       account,
@@ -50,7 +61,7 @@ export const accountModel = {
 };
 
 export const categoryModel = {
-  validate(category, { update }: { update?: boolean } = {}) {
+  validate(category: CategoryEntity, { update }: { update?: boolean } = {}) {
     requiredFields(
       'category',
       category,
@@ -64,7 +75,10 @@ export const categoryModel = {
 };
 
 export const categoryGroupModel = {
-  validate(categoryGroup, { update }: { update?: boolean } = {}) {
+  validate(
+    categoryGroup: CategoryGroupEntity,
+    { update }: { update?: boolean } = {},
+  ) {
     requiredFields(
       'categoryGroup',
       categoryGroup,
@@ -78,78 +92,8 @@ export const categoryGroupModel = {
 };
 
 export const payeeModel = {
-  validate(payee, { update }: { update?: boolean } = {}) {
+  validate(payee: PayeeEntity, { update }: { update?: boolean } = {}) {
     requiredFields('payee', payee, ['name'], update);
     return payee;
   },
 };
-
-export const transactionModel = {
-  validate(trans, { update }: { update?: boolean } = {}) {
-    requiredFields('transaction', trans, ['date', 'acct'], update);
-
-    if ('date' in trans) {
-      // Make sure it's the right format, and also do a sanity check.
-      // Really old dates can mess up the system and can happen by
-      // accident
-      if (
-        trans.date.match(/^\d{4}-\d{2}-\d{2}$/) == null ||
-        trans.date < '2000-01-01'
-      ) {
-        throw new Error('Invalid transaction date: ' + trans.date);
-      }
-    }
-
-    return trans;
-  },
-
-  toJS(row) {
-    // Check a non-important field that typically wouldn't be passed in
-    // manually, and use it as a smoke test to see if this is a
-    // fully-formed transaction or not.
-    if (!('location' in row)) {
-      throw new Error(
-        'A full transaction is required to be passed to `toJS`. Instead got: ' +
-          JSON.stringify(row),
-      );
-    }
-
-    const trans = { ...row };
-    trans.error = row.error ? JSON.parse(row.error) : null;
-    trans.isParent = row.isParent === 1 ? true : false;
-    trans.isChild = row.isChild === 1 ? true : false;
-    trans.starting_balance_flag =
-      row.starting_balance_flag === 1 ? true : false;
-    trans.cleared = row.cleared === 1 ? true : false;
-    trans.pending = row.pending === 1 ? true : false;
-    trans.date = trans.date && fromDateRepr(trans.date);
-    return trans;
-  },
-
-  fromJS(trans) {
-    const row = { ...trans };
-    if ('error' in row) {
-      row.error = trans.error ? JSON.stringify(trans.error) : null;
-    }
-    if ('isParent' in row) {
-      row.isParent = trans.isParent ? 1 : 0;
-    }
-    if ('isChild' in row) {
-      row.isChild = trans.isChild ? 1 : 0;
-    }
-    if ('cleared' in row) {
-      row.cleared = trans.cleared ? 1 : 0;
-    }
-    if ('pending' in row) {
-      row.pending = trans.pending ? 1 : 0;
-    }
-    if ('starting_balance_flag' in row) {
-      row.starting_balance_flag = trans.starting_balance_flag ? 1 : 0;
-    }
-    if ('date' in row) {
-      row.date = toDateRepr(trans.date);
-    }
-
-    return row;
-  },
-};
diff --git a/packages/loot-core/src/server/reports/app.ts b/packages/loot-core/src/server/reports/app.ts
index 612bc23ba73dabe97122ebab496c171803829296..8ee59b9127e6e8d5624b9d5350b072c425dca258 100644
--- a/packages/loot-core/src/server/reports/app.ts
+++ b/packages/loot-core/src/server/reports/app.ts
@@ -15,7 +15,7 @@ import { ReportsHandlers } from './types/handlers';
 
 const reportModel = {
   validate(report: CustomReportEntity, { update }: { update?: boolean } = {}) {
-    requiredFields('reports', report, ['conditions'], update);
+    requiredFields('reports', report, ['conditionsOp'], update);
 
     if (!update || 'conditionsOp' in report) {
       if (!['and', 'or'].includes(report.conditionsOp)) {
diff --git a/packages/loot-core/src/server/sync/index.ts b/packages/loot-core/src/server/sync/index.ts
index 0cd7386bb4b45440638d36778a59ca7948ae33d7..81a4679df7c3cd40078380d5e5f4761273ffc299 100644
--- a/packages/loot-core/src/server/sync/index.ts
+++ b/packages/loot-core/src/server/sync/index.ts
@@ -142,8 +142,7 @@ async function fetchAll(table, ids) {
           message: error.message,
           stack: error.stack,
         },
-        sql,
-        params: partIds,
+        query: { sql, params: partIds },
       });
     }
   }
diff --git a/upcoming-release-notes/2247.md b/upcoming-release-notes/2247.md
new file mode 100644
index 0000000000000000000000000000000000000000..5aea7a9d7b2e1b5695cc6e31ee3f31f1a5d087e0
--- /dev/null
+++ b/upcoming-release-notes/2247.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+TypeScript: making some files comply with strict TS.