From ff11399180094fd050f4e10785924dab91b191bf Mon Sep 17 00:00:00 2001
From: Matiss Janis Aboltins <matiss@mja.lv>
Date: Thu, 18 Jan 2024 08:23:50 +0000
Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(typescript)=20fixing=20st?=
 =?UTF-8?q?rictNullChecks=3Dtrue=20issues=20(#2228)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* ♻️ (typescript) fixing strictNullChecks=true issues

* Patch test-helpers

* Strict patch
---
 packages/loot-core/src/shared/async.ts        | 21 +++++----
 packages/loot-core/src/shared/months.ts       |  8 ++--
 packages/loot-core/src/shared/query.ts        |  2 +-
 packages/loot-core/src/shared/schedules.ts    | 10 ++--
 packages/loot-core/src/shared/test-helpers.ts | 47 +++++++++++--------
 upcoming-release-notes/2228.md                |  6 +++
 6 files changed, 56 insertions(+), 38 deletions(-)
 create mode 100644 upcoming-release-notes/2228.md

diff --git a/packages/loot-core/src/shared/async.ts b/packages/loot-core/src/shared/async.ts
index 70cf5150d..13ad731dc 100644
--- a/packages/loot-core/src/shared/async.ts
+++ b/packages/loot-core/src/shared/async.ts
@@ -4,24 +4,25 @@ import { type HandlerFunctions } from '../types/handlers';
 export function sequential<T extends HandlerFunctions>(
   fn: T,
 ): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>> {
-  const sequenceState = {
+  const sequenceState: {
+    running: Promise<Awaited<ReturnType<T>>> | null;
+    queue: Array<{ args: Parameters<T>; resolve; reject }>;
+  } = {
     running: null,
     queue: [],
   };
 
   function pump() {
-    if (sequenceState.queue.length > 0) {
-      const next = sequenceState.queue.shift();
+    const next = sequenceState.queue.shift();
+    if (next !== undefined) {
       run(next.args, next.resolve, next.reject);
     } else {
       sequenceState.running = null;
     }
   }
 
-  function run(args, resolve, reject) {
-    sequenceState.running = fn.apply(null, args);
-
-    sequenceState.running.then(
+  function run(args: Parameters<T>, resolve, reject) {
+    sequenceState.running = fn.apply(null, args).then(
       val => {
         pump();
         resolve(val);
@@ -48,9 +49,9 @@ export function sequential<T extends HandlerFunctions>(
 
 export function once<T extends HandlerFunctions>(
   fn: T,
-): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>> {
-  let promise = null;
-  return (...args) => {
+): (...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>> | null {
+  let promise: Promise<Awaited<ReturnType<T>>> | null = null;
+  return (...args: Parameters<T>) => {
     if (!promise) {
       promise = fn.apply(null, args).finally(() => {
         promise = null;
diff --git a/packages/loot-core/src/shared/months.ts b/packages/loot-core/src/shared/months.ts
index a481e0a03..5655104ea 100644
--- a/packages/loot-core/src/shared/months.ts
+++ b/packages/loot-core/src/shared/months.ts
@@ -183,7 +183,7 @@ export function _range(
   end: DateLike,
   inclusive = false,
 ): string[] {
-  const months = [];
+  const months: string[] = [];
   let month = monthFromDate(start);
   while (d.isBefore(_parse(month), _parse(end))) {
     months.push(month);
@@ -210,15 +210,15 @@ export function _dayRange(
   end: DateLike,
   inclusive = false,
 ): string[] {
-  const days = [];
+  const days: string[] = [];
   let day = start;
   while (d.isBefore(_parse(day), _parse(end))) {
-    days.push(day);
+    days.push(dayFromDate(day));
     day = addDays(day, 1);
   }
 
   if (inclusive) {
-    days.push(day);
+    days.push(dayFromDate(day));
   }
 
   return days;
diff --git a/packages/loot-core/src/shared/query.ts b/packages/loot-core/src/shared/query.ts
index bf5dd06ee..0dd129b58 100644
--- a/packages/loot-core/src/shared/query.ts
+++ b/packages/loot-core/src/shared/query.ts
@@ -1,6 +1,6 @@
 // @ts-strict-ignore
 export type QueryState = {
-  filterExpressions: Array<unknown>;
+  filterExpressions: Array<string>;
   selectExpressions: Array<unknown>;
   groupExpressions: Array<unknown>;
   orderExpressions: Array<unknown>;
diff --git a/packages/loot-core/src/shared/schedules.ts b/packages/loot-core/src/shared/schedules.ts
index 23dbd9c4e..fa711d5c3 100644
--- a/packages/loot-core/src/shared/schedules.ts
+++ b/packages/loot-core/src/shared/schedules.ts
@@ -4,7 +4,11 @@ import type { IRuleOptions } from '@rschedule/core';
 import * as monthUtils from './months';
 import { q } from './query';
 
-export function getStatus(nextDate, completed, hasTrans) {
+export function getStatus(
+  nextDate: string,
+  completed: boolean,
+  hasTrans: boolean,
+) {
   const today = monthUtils.currentDay();
 
   if (completed) {
@@ -45,7 +49,7 @@ export function getHasTransactionsQuery(schedules) {
     .select(['schedule', 'date']);
 }
 
-function makeNumberSuffix(num) {
+function makeNumberSuffix(num: number) {
   // Slight abuse of date-fns to turn a number like "1" into the full
   // form "1st" but formatting a date with that number
   return monthUtils.format(new Date(2020, 0, num, 12), 'do');
@@ -128,7 +132,7 @@ export function getRecurringDescription(config, dateFormat) {
 
         desc += ' on the ';
 
-        const strs = [];
+        const strs: string[] = [];
 
         const uniqueDays = new Set(patterns.map(p => p.type));
         const isSameDay = uniqueDays.size === 1 && !uniqueDays.has('day');
diff --git a/packages/loot-core/src/shared/test-helpers.ts b/packages/loot-core/src/shared/test-helpers.ts
index ec73e0523..fbdf98896 100644
--- a/packages/loot-core/src/shared/test-helpers.ts
+++ b/packages/loot-core/src/shared/test-helpers.ts
@@ -1,9 +1,8 @@
-// @ts-strict-ignore
-export let tracer = null;
+export let tracer: null | ReturnType<typeof execTracer> = null;
 
-function timeout(promise, n) {
-  let resolve;
-  const timeoutPromise = new Promise(_ => (resolve = _));
+function timeout<T extends Promise<unknown>>(promise: T, n: number) {
+  let resolve: (response: string) => void;
+  const timeoutPromise = new Promise<string>(_ => (resolve = _));
   const timer = setTimeout(() => resolve(`timeout(${n})`), n);
 
   return Promise.race([
@@ -19,16 +18,20 @@ export function resetTracer() {
   tracer = execTracer();
 }
 
-export function execTracer() {
-  const queue = [];
+export function execTracer<T>() {
+  const queue: Array<{ name: string; data?: T }> = [];
   let hasStarted = false;
-  let waitingFor = null;
+  let waitingFor: null | {
+    name: string;
+    reject: (error: Error) => void;
+    resolve: (data?: T) => void;
+  } = null;
   let ended = false;
 
   const log = false;
 
   return {
-    event(name: string, data?: unknown) {
+    event(name: string, data?: T) {
       if (!hasStarted) {
         return;
       } else if (log) {
@@ -57,7 +60,7 @@ export function execTracer() {
       }
     },
 
-    wait(name) {
+    wait(name: string) {
       if (waitingFor) {
         throw new Error(
           `Already waiting for ${waitingFor.name}, cannot wait for multiple events`,
@@ -69,18 +72,22 @@ export function execTracer() {
       });
     },
 
-    expectWait(name, data) {
+    expectWait(name: string, data?: T) {
       if (!hasStarted) {
         throw new Error(`Expected “${name}” but tracer hasn’t started yet`);
       } else if (log) {
         console.log(`--- expectWait(${name}) ---`);
       }
 
-      let promise = this.wait(name);
+      const promise = this.wait(name);
       if (data === undefined) {
         // We want to ignore the result
-        promise = promise.then(() => true);
-        data = true;
+        return expect(
+          timeout(
+            promise.then(() => true),
+            1000,
+          ),
+        ).resolves.toEqual(true);
       }
 
       if (typeof data === 'function') {
@@ -97,20 +104,20 @@ export function execTracer() {
       }
     },
 
-    expectNow(name, data) {
+    expectNow(name: string, data?: T) {
       if (!hasStarted) {
         throw new Error(`Expected “${name}” but tracer hasn’t started yet`);
       } else if (log) {
         console.log(`--- expectNow(${name}) ---`);
       }
 
-      if (queue.length === 0) {
+      const entry = queue.shift();
+
+      if (!entry) {
         throw new Error(
           `Expected event “${name}” but none found - has it happened yet?`,
         );
-      } else if (queue[0].name === name) {
-        const entry = queue.shift();
-
+      } else if (entry.name === name) {
         if (typeof data === 'function') {
           data(entry.data);
         } else {
@@ -123,7 +130,7 @@ export function execTracer() {
       }
     },
 
-    expect(name: string, data?: unknown) {
+    expect(name: string, data?: T) {
       if (queue.length === 0) {
         return this.expectWait(name, data);
       }
diff --git a/upcoming-release-notes/2228.md b/upcoming-release-notes/2228.md
new file mode 100644
index 000000000..e12eb6fb1
--- /dev/null
+++ b/upcoming-release-notes/2228.md
@@ -0,0 +1,6 @@
+---
+category: Maintenance
+authors: [MatissJanis]
+---
+
+TypeScript: fix some `strictNullChecks: true` issues (pt.2)
-- 
GitLab