From 8a15caf42dc4cfecd6b82f24712e90c914d385c2 Mon Sep 17 00:00:00 2001
From: Matiss Janis Aboltins <matiss@mja.lv>
Date: Fri, 15 Dec 2023 18:09:36 +0000
Subject: [PATCH] :recycle: (TypeScript) fix strictFunctionTypes violations (pt
 1) (#2065)

---
 .../src/server/budget/types/handlers.d.ts     | 44 ++++++++++++++-----
 packages/loot-core/src/server/mutators.ts     |  5 +--
 packages/loot-core/src/server/undo.ts         | 17 +++----
 packages/loot-core/src/shared/async.ts        | 14 +++---
 .../loot-core/src/types/api-handlers.d.ts     |  4 +-
 packages/loot-core/src/types/handlers.d.ts    |  2 +
 .../loot-core/src/types/server-handlers.d.ts  |  2 +-
 tsconfig.json                                 |  2 +
 upcoming-release-notes/2065.md                |  6 +++
 9 files changed, 60 insertions(+), 36 deletions(-)
 create mode 100644 upcoming-release-notes/2065.md

diff --git a/packages/loot-core/src/server/budget/types/handlers.d.ts b/packages/loot-core/src/server/budget/types/handlers.d.ts
index bb3f69640..66c2f72c7 100644
--- a/packages/loot-core/src/server/budget/types/handlers.d.ts
+++ b/packages/loot-core/src/server/budget/types/handlers.d.ts
@@ -5,13 +5,13 @@ export interface BudgetHandlers {
     category: string /* category id */;
     month: string;
     amount: number;
-  }) => Promise<unknown>;
+  }) => Promise<void>;
 
-  'budget/copy-previous-month': (...args: unknown[]) => Promise<unknown>;
+  'budget/copy-previous-month': (arg: { month: string }) => Promise<void>;
 
-  'budget/set-zero': (...args: unknown[]) => Promise<unknown>;
+  'budget/set-zero': (arg: { month: string }) => Promise<void>;
 
-  'budget/set-3month-avg': (...args: unknown[]) => Promise<unknown>;
+  'budget/set-3month-avg': (arg: { month: string }) => Promise<void>;
 
   'budget/check-templates': () => Promise<Notification>;
 
@@ -27,17 +27,37 @@ export interface BudgetHandlers {
     month: string;
   }) => Promise<Notification>;
 
-  'budget/hold-for-next-month': (...args: unknown[]) => Promise<unknown>;
+  'budget/hold-for-next-month': (arg: {
+    month: string;
+    amount: number;
+  }) => Promise<boolean>;
 
-  'budget/reset-hold': (...args: unknown[]) => Promise<unknown>;
+  'budget/reset-hold': (arg: { month: string }) => Promise<void>;
 
-  'budget/cover-overspending': (...args: unknown[]) => Promise<unknown>;
+  'budget/cover-overspending': (arg: {
+    month: string;
+    to: string;
+    from: string;
+  }) => Promise<void>;
 
-  'budget/transfer-available': (...args: unknown[]) => Promise<unknown>;
+  'budget/transfer-available': (arg: {
+    month: string;
+    amount: number;
+    category: string;
+  }) => Promise<void>;
 
-  'budget/transfer-category': (...args: unknown[]) => Promise<unknown>;
+  'budget/transfer-category': (arg: {
+    month: string;
+    amount: number;
+    to: string;
+    from: string;
+  }) => Promise<void>;
 
-  'budget/set-carryover': (...args: unknown[]) => Promise<unknown>;
+  'budget/set-carryover': (arg: {
+    startMonth: string;
+    category: string;
+    flag: boolean;
+  }) => Promise<void>;
 
   'budget/apply-single-template': (arg: {
     month: string;
@@ -48,10 +68,10 @@ export interface BudgetHandlers {
     month: string;
     N: number;
     category: string; //category id
-  }) => Promise<unknown>;
+  }) => Promise<void>;
 
   'budget/copy-single-month': (arg: {
     month: string;
     category: string; //category id
-  }) => Promise<unknown>;
+  }) => Promise<void>;
 }
diff --git a/packages/loot-core/src/server/mutators.ts b/packages/loot-core/src/server/mutators.ts
index 9d85c0861..3988e5c58 100644
--- a/packages/loot-core/src/server/mutators.ts
+++ b/packages/loot-core/src/server/mutators.ts
@@ -1,5 +1,6 @@
 import { captureException, captureBreadcrumb } from '../platform/exceptions';
 import { sequential } from '../shared/async';
+import { type HandlerFunctions } from '../types/handlers';
 
 const runningMethods = new Set();
 
@@ -9,9 +10,7 @@ let globalMutationsEnabled = false;
 
 let _latestHandlerNames = [];
 
-export function mutator<T extends (...args: unknown[]) => unknown>(
-  handler: T,
-): T {
+export function mutator<T extends HandlerFunctions>(handler: T): T {
   mutatingMethods.set(handler, true);
   return handler;
 }
diff --git a/packages/loot-core/src/server/undo.ts b/packages/loot-core/src/server/undo.ts
index faf5fa927..ac12e9c38 100644
--- a/packages/loot-core/src/server/undo.ts
+++ b/packages/loot-core/src/server/undo.ts
@@ -2,6 +2,7 @@ import { Timestamp } from '@actual-app/crdt';
 
 import * as connection from '../platform/server/connection';
 import { getIn } from '../shared/util';
+import { type HandlerFunctions } from '../types/handlers';
 
 import { withMutatorContext, getMutatorContext } from './mutators';
 import { Message, sendMessages } from './sync';
@@ -89,18 +90,10 @@ export function withUndo<T>(
   );
 }
 
-// for some reason `void` is not inferred properly without this overload
-export function undoable<Args extends unknown[]>(
-  func: (...args: Args) => Promise<void>,
-): (...args: Args) => Promise<void>;
-export function undoable<
-  Args extends unknown[],
-  Return extends Promise<unknown>,
->(func: (...args: Args) => Return): (...args: Args) => Return;
-export function undoable(func: (...args: unknown[]) => Promise<unknown>) {
-  return (...args: unknown[]) => {
-    return withUndo(() => {
-      return func(...args);
+export function undoable<T extends HandlerFunctions>(func: T) {
+  return (...args: Parameters<T>) => {
+    return withUndo<Awaited<ReturnType<T>>>(() => {
+      return func.apply(null, args);
     });
   };
 }
diff --git a/packages/loot-core/src/shared/async.ts b/packages/loot-core/src/shared/async.ts
index 718204f36..0e6a0c922 100644
--- a/packages/loot-core/src/shared/async.ts
+++ b/packages/loot-core/src/shared/async.ts
@@ -1,4 +1,6 @@
-export function sequential<T extends (...args: unknown[]) => unknown>(
+import { type HandlerFunctions } from '../types/handlers';
+
+export function sequential<T extends HandlerFunctions>(
   fn: T,
 ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>> {
   const sequenceState = {
@@ -16,7 +18,7 @@ export function sequential<T extends (...args: unknown[]) => unknown>(
   }
 
   function run(args, resolve, reject) {
-    sequenceState.running = fn(...args);
+    sequenceState.running = fn.apply(null, args);
 
     sequenceState.running.then(
       val => {
@@ -43,13 +45,13 @@ export function sequential<T extends (...args: unknown[]) => unknown>(
   };
 }
 
-export function once<T extends (...args: unknown[]) => Promise<unknown>>(
+export function once<T extends HandlerFunctions>(
   fn: T,
 ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>> {
   let promise = null;
-  const onceFn = (...args: Parameters<T>): Promise<Awaited<ReturnType<T>>> => {
+  return (...args) => {
     if (!promise) {
-      promise = fn(...args).finally(() => {
+      promise = fn.apply(null, args).finally(() => {
         promise = null;
       });
       return promise;
@@ -57,6 +59,4 @@ export function once<T extends (...args: unknown[]) => Promise<unknown>>(
 
     return promise;
   };
-
-  return onceFn;
 }
diff --git a/packages/loot-core/src/types/api-handlers.d.ts b/packages/loot-core/src/types/api-handlers.d.ts
index 30e7b5b66..71bc15306 100644
--- a/packages/loot-core/src/types/api-handlers.d.ts
+++ b/packages/loot-core/src/types/api-handlers.d.ts
@@ -1,3 +1,5 @@
+import { type ServerHandlers } from './server-handlers';
+
 export interface ApiHandlers {
   'api/batch-budget-start': () => Promise<unknown>;
 
@@ -5,7 +7,7 @@ export interface ApiHandlers {
 
   'api/load-budget': (
     ...args: Parameters<ServerHandlers['load-budget']>
-  ) => Promise<unknown>;
+  ) => Promise<void>;
 
   'api/download-budget': (arg: { syncId; password }) => Promise<unknown>;
 
diff --git a/packages/loot-core/src/types/handlers.d.ts b/packages/loot-core/src/types/handlers.d.ts
index 2537c0279..7d131d445 100644
--- a/packages/loot-core/src/types/handlers.d.ts
+++ b/packages/loot-core/src/types/handlers.d.ts
@@ -17,3 +17,5 @@ export interface Handlers
     RulesHandlers,
     SchedulesHandlers,
     ToolsHandlers {}
+
+export type HandlerFunctions = Handlers[keyof Handlers];
diff --git a/packages/loot-core/src/types/server-handlers.d.ts b/packages/loot-core/src/types/server-handlers.d.ts
index 1e85f0045..cb1f0c8cf 100644
--- a/packages/loot-core/src/types/server-handlers.d.ts
+++ b/packages/loot-core/src/types/server-handlers.d.ts
@@ -326,7 +326,7 @@ export interface ServerHandlers {
     error?: { message: string; reason: string; meta: unknown };
   }>;
 
-  'load-budget': (arg: { id }) => Promise<{ error }>;
+  'load-budget': (arg: { id: string }) => Promise<{ error }>;
 
   'create-demo-budget': () => Promise<unknown>;
 
diff --git a/tsconfig.json b/tsconfig.json
index 1a4dce7bf..844bcaf11 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -15,6 +15,8 @@
     "downlevelIteration": true,
     // TODO: enable once every file is ts
     // "strict": true,
+    // TODO: enable once the violations are fixed
+    // "strictFunctionTypes": true,
     "noFallthroughCasesInSwitch": true,
     "skipLibCheck": true,
     "jsx": "preserve",
diff --git a/upcoming-release-notes/2065.md b/upcoming-release-notes/2065.md
new file mode 100644
index 000000000..873d58604
--- /dev/null
+++ b/upcoming-release-notes/2065.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+Fixing TypeScript issues when enabling `strictFunctionTypes`.
-- 
GitLab