diff --git a/packages/loot-core/src/server/accounts/rules.ts b/packages/loot-core/src/server/accounts/rules.ts index ef6c78decab..2778f00cdf7 100644 --- a/packages/loot-core/src/server/accounts/rules.ts +++ b/packages/loot-core/src/server/accounts/rules.ts @@ -27,7 +27,7 @@ import { ungroupTransaction, } from '../../shared/transactions'; import { fastSetMerge } from '../../shared/util'; -import { RuleConditionEntity } from '../../types/models'; +import { RuleConditionEntity, RuleEntity } from '../../types/models'; import { RuleError } from '../errors'; import { Schedule as RSchedule } from '../util/rschedule'; @@ -87,7 +87,7 @@ function registerHandlebarsHelpers() { registerHandlebarsHelpers(); -function assert(test, type, msg) { +function assert(test: unknown, type: string, msg: string): asserts test { if (!test) { throw new RuleError(type, msg); } @@ -793,11 +793,11 @@ export function execActions(actions: Action[], transaction) { } export class Rule { - actions; - conditions; + actions: Action[]; + conditions: Condition[]; conditionsOp; - id; - stage; + id?: string; + stage: 'pre' | null | 'post'; constructor({ id, @@ -807,13 +807,13 @@ export class Rule { actions, }: { id?: string; - stage?; + stage?: 'pre' | null | 'post'; conditionsOp; conditions; actions; }) { this.id = id; - this.stage = stage; + this.stage = stage ?? null; this.conditionsOp = conditionsOp; this.conditions = conditions.map( c => new Condition(c.op, c.field, c.value, c.options), @@ -823,7 +823,7 @@ export class Rule { ); } - evalConditions(object) { + evalConditions(object): boolean { if (this.conditions.length === 0) { return false; } @@ -860,11 +860,11 @@ export class Rule { return Object.assign({}, object, changes); } - getId() { + getId(): string | undefined { return this.id; } - serialize() { + serialize(): RuleEntity { return { id: this.id, stage: this.stage, @@ -876,9 +876,9 @@ export class Rule { } export class RuleIndexer { - field; - method; - rules; + field: string; + method?: string; + rules: Map>; constructor({ field, method }: { field: string; method?: string }) { this.field = field; @@ -886,18 +886,18 @@ export class RuleIndexer { this.rules = new Map(); } - getIndex(key) { + getIndex(key: string | null): Set { if (!this.rules.has(key)) { this.rules.set(key, new Set()); } return this.rules.get(key); } - getIndexForValue(value) { + getIndexForValue(value: unknown): Set { return this.getIndex(this.getKey(value) || '*'); } - getKey(value) { + getKey(value: unknown): string | null { if (typeof value === 'string' && value !== '') { if (this.method === 'firstchar') { return value[0].toLowerCase(); @@ -907,7 +907,7 @@ export class RuleIndexer { return null; } - getIndexes(rule) { + getIndexes(rule: Rule): Set[] { const cond = rule.conditions.find(cond => cond.field === this.field); const indexes = []; @@ -930,21 +930,21 @@ export class RuleIndexer { return indexes; } - index(rule) { + index(rule: Rule): void { const indexes = this.getIndexes(rule); indexes.forEach(index => { index.add(rule); }); } - remove(rule) { + remove(rule: Rule): void { const indexes = this.getIndexes(rule); indexes.forEach(index => { index.delete(rule); }); } - getApplicableRules(object) { + getApplicableRules(object): Set { let indexedRules; if (this.field in object) { const key = this.getKey(object[this.field]); @@ -977,7 +977,7 @@ const OP_SCORES: Record = { hasTags: 0, }; -function computeScore(rule) { +function computeScore(rule: Rule): number { const initialScore = rule.conditions.reduce((score, condition) => { if (OP_SCORES[condition.op] == null) { console.log(`Found invalid operation while ranking: ${condition.op}`); @@ -1002,7 +1002,7 @@ function computeScore(rule) { return initialScore; } -function _rankRules(rules) { +function _rankRules(rules: Rule[]): Rule[] { const scores = new Map(); rules.forEach(rule => { scores.set(rule, computeScore(rule)); @@ -1026,7 +1026,7 @@ function _rankRules(rules) { }); } -export function rankRules(rules) { +export function rankRules(rules: Iterable): Rule[] { let pre = []; let normal = []; let post = []; @@ -1051,7 +1051,7 @@ export function rankRules(rules) { return pre.concat(normal).concat(post); } -export function migrateIds(rule, mappings) { +export function migrateIds(rule: Rule, mappings: Map): void { // Go through the in-memory rules and patch up ids that have been // "migrated" to other ids. This is a little tricky, but a lot // easier than trying to keep an up-to-date mapping in the db. This @@ -1103,7 +1103,11 @@ export function migrateIds(rule, mappings) { } // This finds all the rules that reference the `id` -export function iterateIds(rules, fieldName, func) { +export function iterateIds( + rules: Rule[], + fieldName: string, + func: (rule: Rule, id: string) => void | boolean, +): void { let i; ruleiter: for (i = 0; i < rules.length; i++) { diff --git a/packages/loot-core/src/server/accounts/transaction-rules.ts b/packages/loot-core/src/server/accounts/transaction-rules.ts index 20638526323..710f057b03a 100644 --- a/packages/loot-core/src/server/accounts/transaction-rules.ts +++ b/packages/loot-core/src/server/accounts/transaction-rules.ts @@ -547,7 +547,7 @@ export async function applyActions( } export function getRulesForPayee(payeeId) { - const rules = new Set(); + const rules = new Set(); iterateIds(getRules(), 'payee', (rule, id) => { if (id === payeeId) { rules.add(rule); diff --git a/packages/loot-core/src/types/models/rule.d.ts b/packages/loot-core/src/types/models/rule.d.ts index 8e7cf96b014..b3e2b6befd5 100644 --- a/packages/loot-core/src/types/models/rule.d.ts +++ b/packages/loot-core/src/types/models/rule.d.ts @@ -1,8 +1,8 @@ import { type ScheduleEntity } from './schedule'; export interface NewRuleEntity { - stage: string; - conditionsOp: 'any' | 'and'; + stage: 'pre' | null | 'post'; + conditionsOp: 'or' | 'and'; conditions: RuleConditionEntity[]; actions: RuleActionEntity[]; tombstone?: boolean; diff --git a/upcoming-release-notes/3365.md b/upcoming-release-notes/3365.md new file mode 100644 index 00000000000..0f8ad8e2b3a --- /dev/null +++ b/upcoming-release-notes/3365.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [UnderKoen] +--- + +Add more strict types to `account/rules.ts`