Fix some low-hanging-fruit @ts-strict-ignore (#6969)

* Fix low-hanging-fruit `@ts-strict-ignore`

* Add release notes

* A few more
This commit is contained in:
Julian Dominguez-Schatz
2026-02-14 10:58:25 -05:00
committed by GitHub
parent 5943ae3df5
commit 6358345286
10 changed files with 57 additions and 27 deletions

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import React from 'react'; import React from 'react';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
@@ -26,7 +25,9 @@ export function AnimatedRefresh({
}: AnimatedRefreshProps) { }: AnimatedRefreshProps) {
return ( return (
<View <View
style={{ animation: animating ? `${spin} 1s infinite linear` : null }} style={{
animation: animating ? `${spin} 1s infinite linear` : undefined,
}}
> >
<SvgRefresh <SvgRefresh
width={width ? width : 14} width={width ? width : 14}

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import React, { useEffect, useEffectEvent, useRef } from 'react'; import React, { useEffect, useEffectEvent, useRef } from 'react';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -58,7 +57,13 @@ function NarrowNotSupported({
return isNarrowWidth ? null : children; return isNarrowWidth ? null : children;
} }
function WideNotSupported({ children, redirectTo = '/budget' }) { function WideNotSupported({
children,
redirectTo = '/budget',
}: {
redirectTo?: string;
children: ReactElement;
}) {
const { isNarrowWidth } = useResponsive(); const { isNarrowWidth } = useResponsive();
const navigate = useNavigate(); const navigate = useNavigate();
useEffect(() => { useEffect(() => {
@@ -81,7 +86,7 @@ function RouterBehaviors() {
export function FinancesApp() { export function FinancesApp() {
const { isNarrowWidth } = useResponsive(); const { isNarrowWidth } = useResponsive();
useMetaThemeColor(isNarrowWidth ? theme.mobileViewTheme : null); useMetaThemeColor(isNarrowWidth ? theme.mobileViewTheme : undefined);
const dispatch = useDispatch(); const dispatch = useDispatch();
const { t } = useTranslation(); const { t } = useTranslation();

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
@@ -49,7 +48,7 @@ export function Notes({
useEffect(() => { useEffect(() => {
if (focused && editable) { if (focused && editable) {
textAreaRef.current.focus(); textAreaRef.current?.focus();
} }
}, [focused, editable]); }, [focused, editable]);
@@ -71,7 +70,7 @@ export function Notes({
placeholder={t('Notes (markdown supported)')} placeholder={t('Notes (markdown supported)')}
/> />
) : ( ) : (
<Text className={css([markdownStyles, getStyle?.(editable)])}> <Text className={css([markdownStyles, getStyle?.(editable ?? false)])}>
<ReactMarkdown <ReactMarkdown
remarkPlugins={remarkPlugins} remarkPlugins={remarkPlugins}
rehypePlugins={[ rehypePlugins={[

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import React, { useEffect, useEffectEvent, useMemo, useState } from 'react'; import React, { useEffect, useEffectEvent, useMemo, useState } from 'react';
import type { CSSProperties, SetStateAction } from 'react'; import type { CSSProperties, SetStateAction } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@@ -60,7 +59,7 @@ function compileMessage(
if (actions[actionName]) { if (actions[actionName]) {
setLoading(true); setLoading(true);
await actions[actionName](); await actions[actionName]();
onRemove(); onRemove?.();
} }
}} }}
> >
@@ -131,7 +130,13 @@ function Notification({
const error = type === 'error'; const error = type === 'error';
const processedMessage = useMemo( const processedMessage = useMemo(
() => compileMessage(message, messageActions, setOverlayLoading, onRemove), () =>
compileMessage(
message,
messageActions ?? {},
setOverlayLoading,
onRemove,
),
[message, messageActions, onRemove, setOverlayLoading], [message, messageActions, onRemove, setOverlayLoading],
); );

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import React from 'react'; import React from 'react';
import type { ComponentType, CSSProperties, ReactNode } from 'react'; import type { ComponentType, CSSProperties, ReactNode } from 'react';
@@ -12,7 +11,7 @@ import { theme } from '@actual-app/components/theme';
import { View } from '@actual-app/components/view'; import { View } from '@actual-app/components/view';
type AlertProps = { type AlertProps = {
icon?: ComponentType<{ width?: number; style?: CSSProperties }>; icon: ComponentType<{ width?: number; style?: CSSProperties }>;
color?: string; color?: string;
backgroundColor?: string; backgroundColor?: string;
style?: CSSProperties; style?: CSSProperties;

View File

@@ -32,7 +32,7 @@ type IScrollContext = {
const ScrollContext = createContext<IScrollContext | undefined>(undefined); const ScrollContext = createContext<IScrollContext | undefined>(undefined);
type ScrollProviderProps<T extends Element> = { type ScrollProviderProps<T extends Element> = {
scrollableRef: RefObject<T>; scrollableRef: RefObject<T | null>;
isDisabled?: boolean; isDisabled?: boolean;
delayMs?: number; delayMs?: number;
children?: ReactNode; children?: ReactNode;

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
// This file will initialize the app if we are in a real browser // This file will initialize the app if we are in a real browser
// environment (not electron) // environment (not electron)
import './browser-preload'; import './browser-preload';
@@ -86,6 +85,9 @@ window.$query = aqlQuery;
window.$q = q; window.$q = q;
const container = document.getElementById('root'); const container = document.getElementById('root');
if (!container) {
throw new Error('Root container not found');
}
const root = createRoot(container); const root = createRoot(container);
root.render( root.render(
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import type { QueryClient } from '@tanstack/react-query'; import type { QueryClient } from '@tanstack/react-query';
import { t } from 'i18next'; import { t } from 'i18next';
@@ -253,6 +252,12 @@ export function listenForSyncEvent(store: AppStore, queryClient: QueryClient) {
// few things depending on the state, and we try to show an // few things depending on the state, and we try to show an
// appropriate message and call to action to fix it. // appropriate message and call to action to fix it.
const { cloudFileId } = store.getState().prefs.local; const { cloudFileId } = store.getState().prefs.local;
if (!cloudFileId) {
console.error(
'Received file-has-reset or file-has-new-key error but no cloudFileId in prefs',
);
break;
}
notif = { notif = {
title: t('Syncing has been reset on this cloud file'), title: t('Syncing has been reset on this cloud file'),
@@ -277,7 +282,7 @@ export function listenForSyncEvent(store: AppStore, queryClient: QueryClient) {
break; break;
case 'encrypt-failure': case 'encrypt-failure':
case 'decrypt-failure': case 'decrypt-failure':
if (event.meta.isMissingKey) { if (event.meta?.isMissingKey) {
notif = { notif = {
title: t('Missing encryption key'), title: t('Missing encryption key'),
message: t( message: t(

View File

@@ -1,4 +1,3 @@
// @ts-strict-ignore
import { getCurrency } from 'loot-core/shared/currencies'; import { getCurrency } from 'loot-core/shared/currencies';
import type { Currency } from 'loot-core/shared/currencies'; import type { Currency } from 'loot-core/shared/currencies';
import { q } from 'loot-core/shared/query'; import { q } from 'loot-core/shared/query';
@@ -241,11 +240,11 @@ export class CategoryTemplateContext {
// don't overbudget when using a priority unless income category // don't overbudget when using a priority unless income category
if (priority > 0 && available < 0 && !this.category.is_income) { if (priority > 0 && available < 0 && !this.category.is_income) {
this.fullAmount += toBudget; this.fullAmount = (this.fullAmount || 0) + toBudget;
toBudget = Math.max(0, toBudget + available); toBudget = Math.max(0, toBudget + available);
this.toBudgetAmount += toBudget; this.toBudgetAmount += toBudget;
} else { } else {
this.fullAmount += toBudget; this.fullAmount = (this.fullAmount || 0) + toBudget;
this.toBudgetAmount += toBudget; this.toBudgetAmount += toBudget;
} }
return this.category.is_income ? -toBudget : toBudget; return this.category.is_income ? -toBudget : toBudget;
@@ -301,9 +300,9 @@ export class CategoryTemplateContext {
readonly hideDecimal: boolean = false; readonly hideDecimal: boolean = false;
private remainderWeight: number = 0; private remainderWeight: number = 0;
private toBudgetAmount: number = 0; // amount that will be budgeted by the templates private toBudgetAmount: number = 0; // amount that will be budgeted by the templates
private fullAmount: number = null; // the full requested amount, start null for remainder only cats private fullAmount: number | null = null; // the full requested amount, start null for remainder only cats
private isLongGoal: boolean = null; //defaulting the goals to null so templates can be unset private isLongGoal: boolean | null = null; //defaulting the goals to null so templates can be unset
private goalAmount: number = null; private goalAmount: number | null = null;
private fromLastMonth = 0; // leftover from last month private fromLastMonth = 0; // leftover from last month
private limitMet = false; private limitMet = false;
private limitExcess: number = 0; private limitExcess: number = 0;
@@ -593,7 +592,7 @@ export class CategoryTemplateContext {
break; break;
case 'year': case 'year':
// the addYears function doesn't return the month number, so use addMonths // the addYears function doesn't return the month number, so use addMonths
dateShiftFunction = (date, numPeriods) => dateShiftFunction = (date: string | Date, numPeriods: number) =>
monthUtils.addMonths(date, numPeriods * 12); monthUtils.addMonths(date, numPeriods * 12);
break; break;
default: default:
@@ -716,6 +715,11 @@ export class CategoryTemplateContext {
const incomeCat = (await db.getCategories()).find( const incomeCat = (await db.getCategories()).find(
c => c.is_income && c.name.toLowerCase() === cat, c => c.is_income && c.name.toLowerCase() === cat,
); );
if (!incomeCat) {
throw new Error(
`Income category "${template.category}" not found for percentage template`,
);
}
monthlyIncome = await getSheetValue( monthlyIncome = await getSheetValue(
sheetName, sheetName,
`sum-amount-${incomeCat.id}`, `sum-amount-${incomeCat.id}`,
@@ -772,7 +776,7 @@ export class CategoryTemplateContext {
); );
const savedInfo = []; const savedInfo = [];
let totalNeeded = 0; let totalNeeded = 0;
let shortNumMonths; let workingShortNumMonths;
//find shortest time period //find shortest time period
for (let i = 0; i < byTemplates.length; i++) { for (let i = 0; i < byTemplates.length; i++) {
const template = byTemplates[i]; const template = byTemplates[i];
@@ -794,12 +798,16 @@ export class CategoryTemplateContext {
); );
} }
savedInfo.push({ numMonths, period }); savedInfo.push({ numMonths, period });
if (numMonths < shortNumMonths || shortNumMonths === undefined) { if (
shortNumMonths = numMonths; workingShortNumMonths === undefined ||
numMonths < workingShortNumMonths
) {
workingShortNumMonths = numMonths;
} }
} }
// calculate needed funds per template // calculate needed funds per template
const shortNumMonths = workingShortNumMonths || 0;
for (let i = 0; i < byTemplates.length; i++) { for (let i = 0; i < byTemplates.length; i++) {
const template = byTemplates[i]; const template = byTemplates[i];
const numMonths = savedInfo[i].numMonths; const numMonths = savedInfo[i].numMonths;

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [jfdoming]
---
Fix some low-hanging-fruit `@ts-strict-ignore`