From 4850034e6f09f238929f6b28c07aab8127b5cd09 Mon Sep 17 00:00:00 2001 From: Joel Jeremy Marquez Date: Tue, 21 Jan 2025 10:03:54 -0800 Subject: [PATCH] [TypeScript] Add types to amount utils (#4207) * Add types to amount utils * Release notes --- .../ImportTransactionsModal/Transaction.tsx | 10 ++-- .../components/reports/graphs/AreaGraph.tsx | 14 +++-- .../components/reports/graphs/BarGraph.tsx | 14 ++--- .../reports/reports/SpendingCard.tsx | 2 +- packages/loot-core/src/shared/util.ts | 55 +++++++++++++------ upcoming-release-notes/4207.md | 6 ++ 6 files changed, 64 insertions(+), 37 deletions(-) create mode 100644 upcoming-release-notes/4207.md diff --git a/packages/desktop-client/src/components/modals/ImportTransactionsModal/Transaction.tsx b/packages/desktop-client/src/components/modals/ImportTransactionsModal/Transaction.tsx index 6808cf18ed2..e6c325ca9c8 100644 --- a/packages/desktop-client/src/components/modals/ImportTransactionsModal/Transaction.tsx +++ b/packages/desktop-client/src/components/modals/ImportTransactionsModal/Transaction.tsx @@ -218,12 +218,12 @@ export function Transaction({ : {}), }} title={ - inflow === null && outflow === null + outflow === null ? 'Invalid: unable to parse the value' : amountToCurrency(outflow) } > - {amountToCurrency(outflow)} + {amountToCurrency(outflow || 0)} - {amountToCurrency(inflow)} + {amountToCurrency(inflow || 0)} ) : ( @@ -271,7 +271,7 @@ export function Transaction({ : amountToCurrency(amount) } > - {amountToCurrency(amount)} + {amountToCurrency(amount || 0)} )} diff --git a/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx b/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx index f97b9edaf30..fea3afb4291 100644 --- a/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx @@ -33,11 +33,11 @@ import { renderCustomLabel } from './renderCustomLabel'; type PayloadItem = { payload: { date: string; - totalAssets: number | string; - totalDebts: number | string; - netAssets: number | string; - netDebts: number | string; - totalTotals: number | string; + totalAssets: number; + totalDebts: number; + netAssets: number; + netDebts: number; + totalTotals: number; }; }; @@ -141,7 +141,9 @@ const customLabel = ({ ((typeof props.value === 'number' ? props.value : 0) > 0 ? 10 : -10); const textAnchor = props.index === 0 ? 'left' : 'middle'; const display = - props.value !== 0 ? `${amountToCurrencyNoDecimal(props.value)}` : ''; + typeof props.value !== 'string' && props.value !== 0 + ? `${amountToCurrencyNoDecimal(props.value || 0)}` + : ''; const textSize = adjustTextSize({ sized: width, type: 'area' }); return renderCustomLabel(calcX, calcY, textAnchor, display, textSize); diff --git a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx index e32a8414419..bcfc762de98 100644 --- a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx @@ -50,13 +50,13 @@ type PayloadChild = { type PayloadItem = { payload: { name: string; - totalAssets: number | string; - totalDebts: number | string; - netAssets: number | string; - netDebts: number | string; - totalTotals: number | string; - networth: number | string; - totalChange: number | string; + totalAssets: number; + totalDebts: number; + netAssets: number; + netDebts: number; + totalTotals: number; + networth: number; + totalChange: number; children: [PayloadChild]; }; }; diff --git a/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx b/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx index cebea01c081..e7916fc8360 100644 --- a/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx +++ b/packages/desktop-client/src/components/reports/reports/SpendingCard.tsx @@ -136,7 +136,7 @@ export function SpendingCard({ {data && (difference && difference > 0 ? '+' : '') + - amountToCurrency(difference)} + amountToCurrency(difference || 0)} diff --git a/packages/loot-core/src/shared/util.ts b/packages/loot-core/src/shared/util.ts index 92ea4e4b4a5..9c603384b57 100644 --- a/packages/loot-core/src/shared/util.ts +++ b/packages/loot-core/src/shared/util.ts @@ -329,6 +329,21 @@ export function getNumberFormat({ // Number utilities +/** + * The exact amount. + */ +export type Amount = number; +/** + * The exact amount that is formatted based on the configured number format. + * For example, 123.45 would be '123.45' or '123,45'. + */ +export type CurrencyAmount = string; +/** + * The amount with the decimal point removed. + * For example, 123.45 would be 12345. + */ +export type IntegerAmount = number; + // We dont use `Number.MAX_SAFE_NUMBER` and such here because those // numbers are so large that it's not safe to convert them to floats // (i.e. N / 100). For example, `9007199254740987 / 100 === @@ -353,39 +368,41 @@ export function safeNumber(value: number) { return value; } -export function toRelaxedNumber(value: string) { - return integerToAmount(currencyToInteger(value) || 0); +export function toRelaxedNumber(currencyAmount: CurrencyAmount): Amount { + return integerToAmount(currencyToInteger(currencyAmount) || 0); } export function integerToCurrency( - n: number, + integerAmount: IntegerAmount, formatter = getNumberFormat().formatter, ) { - return formatter.format(safeNumber(n) / 100); + return formatter.format(safeNumber(integerAmount) / 100); } -export function amountToCurrency(n) { - return getNumberFormat().formatter.format(n); +export function amountToCurrency(amount: Amount): CurrencyAmount { + return getNumberFormat().formatter.format(amount); } -export function amountToCurrencyNoDecimal(n) { +export function amountToCurrencyNoDecimal(amount: Amount): CurrencyAmount { return getNumberFormat({ ...numberFormatConfig, hideFraction: true, - }).formatter.format(n); + }).formatter.format(amount); } -export function currencyToAmount(str: string) { +export function currencyToAmount( + currencyAmount: CurrencyAmount, +): Amount | null { let amount; if (getNumberFormat().separatorRegex) { amount = parseFloat( - str + currencyAmount .replace(getNumberFormat().regex, '') .replace(getNumberFormat().separatorRegex, '.'), ); } else { amount = parseFloat( - str + currencyAmount .replace(getNumberFormat().regex, '') .replace(getNumberFormat().separator, '.'), ); @@ -393,12 +410,14 @@ export function currencyToAmount(str: string) { return isNaN(amount) ? null : amount; } -export function currencyToInteger(str: string) { - const amount = currencyToAmount(str); +export function currencyToInteger( + currencyAmount: CurrencyAmount, +): IntegerAmount | null { + const amount = currencyToAmount(currencyAmount); return amount == null ? null : amountToInteger(amount); } -export function stringToInteger(str: string) { +export function stringToInteger(str: string): number | null { const amount = parseInt(str.replace(/[^-0-9.,]/g, '')); if (!isNaN(amount)) { return amount; @@ -406,12 +425,12 @@ export function stringToInteger(str: string) { return null; } -export function amountToInteger(n: number) { - return Math.round(n * 100); +export function amountToInteger(amount: Amount): IntegerAmount { + return Math.round(amount * 100); } -export function integerToAmount(n) { - return parseFloat((safeNumber(n) / 100).toFixed(2)); +export function integerToAmount(integerAmount: IntegerAmount): Amount { + return parseFloat((safeNumber(integerAmount) / 100).toFixed(2)); } // This is used when the input format could be anything (from diff --git a/upcoming-release-notes/4207.md b/upcoming-release-notes/4207.md new file mode 100644 index 00000000000..e3bcb7fd8f6 --- /dev/null +++ b/upcoming-release-notes/4207.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [joel-jeremy] +--- + +Add type to the the amount utils to clarify what's the difference between amount, integer amount, and currency.