From 85804e0b2690eed1e7854e916d159494e9cbcce6 Mon Sep 17 00:00:00 2001 From: hidden4003 Date: Thu, 21 Sep 2023 20:55:17 +0300 Subject: [PATCH] Add eslint rules (#644) * Add @typescript-eslint/no-explicit-any (issue #626) * Remove rule and extend the plugin * Add @typescript-eslint/array-type --- .eslintrc.json | 4 +- .../BackgroundImagePlaceholderDiv.tsx | 2 +- src/components/CharacterImage.tsx | 2 +- .../Collection/AnidbDescription.tsx | 2 +- src/components/Collection/CardViewItem.tsx | 2 +- src/components/Dialogs/ActionsModal.tsx | 10 ++-- src/components/Dialogs/FiltersModal.tsx | 6 +-- src/components/Dialogs/ImportFolderModal.tsx | 30 ++++++++---- src/components/Dialogs/LanguagesModal.tsx | 4 +- src/components/Dialogs/QueueModal.tsx | 2 +- src/components/DnDList/DnDList.tsx | 2 +- src/components/Input/Button.tsx | 4 +- src/components/Input/Checkbox.tsx | 2 +- src/components/Input/Input.tsx | 6 +-- src/components/Input/InputSmall.tsx | 4 +- src/components/Input/Select.tsx | 4 +- src/components/Input/SelectEpisodeList.tsx | 6 +-- src/components/Input/SelectSmall.tsx | 4 +- src/components/Layout/MenuItem.tsx | 2 +- src/components/Panels/ModalPanel.tsx | 2 +- src/components/Panels/ShokoPanel.tsx | 4 +- src/components/Settings/AvatarEditorModal.tsx | 3 +- src/components/TransitionDiv.tsx | 2 +- src/components/TreeView/TreeNode.tsx | 2 +- .../Unrecognized/ManuallyLinkedFilesRow.tsx | 2 +- .../Utilities/Unrecognized/MenuButton.tsx | 2 +- src/components/Utilities/UtilitiesTable.tsx | 2 +- src/core/localStorage.ts | 6 +-- src/core/middlewares/rtkQueryError.ts | 2 +- src/core/middlewares/signalr.ts | 2 +- src/core/rtkQuery/externalApi.ts | 4 +- src/core/rtkQuery/splitV3Api/collectionApi.ts | 6 +-- src/core/rtkQuery/splitV3Api/dashboardApi.ts | 10 ++-- .../rtkQuery/splitV3Api/importFolderApi.ts | 2 +- src/core/rtkQuery/splitV3Api/seriesApi.ts | 18 +++---- src/core/rtkQuery/splitV3Api/tagsApi.ts | 4 +- src/core/rtkQuery/splitV3Api/userApi.ts | 8 ++-- src/core/slices/collection.ts | 4 +- src/core/slices/firstrun.ts | 2 +- src/core/slices/misc.ts | 4 +- src/core/types/api/file.ts | 36 +++++++------- src/core/types/api/init.ts | 6 ++- src/core/types/api/settings.ts | 10 ++-- src/core/types/api/user.ts | 4 +- src/core/types/api/webui.ts | 2 +- src/core/util.ts | 48 +++++++++++-------- src/core/utilities/auto-match-logic.ts | 4 +- src/pages/collection/GroupView.tsx | 2 +- src/pages/collection/series/SeriesCredits.tsx | 2 +- src/pages/collection/series/SeriesImages.tsx | 2 +- src/pages/dashboard/panels/MediaType.tsx | 6 +-- src/pages/dashboard/panels/QueueProcessor.tsx | 2 +- src/pages/error/ErrorPage.tsx | 2 +- src/pages/firstrun/AniDBAccount.tsx | 2 +- src/pages/firstrun/FirstRunPage.tsx | 4 +- src/pages/firstrun/ImportFolders.tsx | 10 ++-- .../firstrun/MetadataSourcesTabs/AniDBTab.tsx | 2 +- .../MetadataSourcesTabs/MovieDBTab.tsx | 2 +- .../firstrun/MetadataSourcesTabs/TvDBTab.tsx | 2 +- src/pages/settings/SettingsPage.tsx | 4 +- src/pages/settings/tabs/AniDBSettings.tsx | 22 ++++----- src/pages/settings/tabs/GeneralSettings.tsx | 3 +- .../settings/tabs/UserManagementSettings.tsx | 7 +-- .../utilities/SeriesWithoutFilesUtility.tsx | 9 +++- src/pages/utilities/UnrecognizedUtility.tsx | 1 + .../IgnoredFilesTab.tsx | 9 +++- .../ManuallyLinkedTab.tsx | 2 +- 67 files changed, 222 insertions(+), 172 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index dd7cb2675..85b2274f2 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,7 +3,8 @@ "airbnb", "airbnb-typescript", "airbnb/hooks", - "plugin:tailwindcss/recommended" + "plugin:tailwindcss/recommended", + "plugin:@typescript-eslint/recommended" ], "ignorePatterns": [ "/*", @@ -45,6 +46,7 @@ "error", "type" ], + "@typescript-eslint/array-type": "error", "arrow-parens": [ "error", "as-needed", diff --git a/src/components/BackgroundImagePlaceholderDiv.tsx b/src/components/BackgroundImagePlaceholderDiv.tsx index 9a38f3cf7..e58fba96d 100644 --- a/src/components/BackgroundImagePlaceholderDiv.tsx +++ b/src/components/BackgroundImagePlaceholderDiv.tsx @@ -6,7 +6,7 @@ import cx from 'classnames'; import type { ImageType } from '@/core/types/api/common'; type Props = { - children?: any; + children?: React.ReactNode; className?: string; image: ImageType | null; hidePlaceholderOnHover?: boolean; diff --git a/src/components/CharacterImage.tsx b/src/components/CharacterImage.tsx index 8a45d164b..ac205ab3c 100644 --- a/src/components/CharacterImage.tsx +++ b/src/components/CharacterImage.tsx @@ -4,7 +4,7 @@ import { Icon } from '@mdi/react'; import cx from 'classnames'; type Props = { - children?: any; + children?: React.ReactNode; className?: string; imageSrc: string | null; hidePlaceholderOnHover?: boolean; diff --git a/src/components/Collection/AnidbDescription.tsx b/src/components/Collection/AnidbDescription.tsx index e98031448..dcf80020e 100644 --- a/src/components/Collection/AnidbDescription.tsx +++ b/src/components/Collection/AnidbDescription.tsx @@ -15,7 +15,7 @@ const AnidbDescription = ({ text }: { text: string }) => { .replaceAll(RemoveSummaryRegex, '') .replaceAll(CleanMultiEmptyLinesRegex, '\n'); - const lines = [] as Array; + const lines = [] as React.ReactNode[]; let prevPos = 0; let pos = 0; let link = LinkRegex.exec(cleanedText); diff --git a/src/components/Collection/CardViewItem.tsx b/src/components/Collection/CardViewItem.tsx index e9b452034..582770dd2 100644 --- a/src/components/Collection/CardViewItem.tsx +++ b/src/components/Collection/CardViewItem.tsx @@ -25,7 +25,7 @@ import type { SeriesSizesFileSourcesType } from '@/core/types/api/series'; import type { WebuiGroupExtra } from '@/core/types/api/webui'; const renderFileSources = (sources: SeriesSizesFileSourcesType): string => { - const output: Array = []; + const output: string[] = []; forEach(sources, (source, type) => { if (source !== 0) output.push(type); }); diff --git a/src/components/Dialogs/ActionsModal.tsx b/src/components/Dialogs/ActionsModal.tsx index 239bdc7b5..f4196603a 100644 --- a/src/components/Dialogs/ActionsModal.tsx +++ b/src/components/Dialogs/ActionsModal.tsx @@ -11,6 +11,7 @@ import toast from '@/components/Toast'; import TransitionDiv from '@/components/TransitionDiv'; import quickActions from '@/core/quick-actions'; import { useRunActionMutation } from '@/core/rtkQuery/splitV3Api/actionsApi'; +import { isErrorWithMessage } from '@/core/util'; const actions = { import: { @@ -89,10 +90,13 @@ const Action = ({ actionKey }: { actionKey: string }) => { const [runActionTrigger] = useRunActionMutation(); const runAction = async (name: string, action) => { - // TODO: figure out better type for this - const result: any = await runActionTrigger(action); - if (!result.error) { + try { + await runActionTrigger(action); toast.success(`Running action "${name}"`); + } catch (err) { + if (isErrorWithMessage(err)) { + console.error(err.message); + } } }; diff --git a/src/components/Dialogs/FiltersModal.tsx b/src/components/Dialogs/FiltersModal.tsx index b820b8be7..5007f371f 100644 --- a/src/components/Dialogs/FiltersModal.tsx +++ b/src/components/Dialogs/FiltersModal.tsx @@ -17,9 +17,9 @@ type Props = { function FiltersModal({ onClose, show }: Props) { const [trigger, filtersResult] = useLazyGetTopFiltersQuery({}); const [triggerSubFilter, subFiltersResult] = useLazyGetFiltersQuery({}); - const filters: Array = filtersResult?.data?.List ?? [] as Array; - const subFilters: Array = useMemo( - () => subFiltersResult?.data?.List ?? [] as Array, + const filters: CollectionFilterType[] = filtersResult?.data?.List ?? [] as CollectionFilterType[]; + const subFilters: CollectionFilterType[] = useMemo( + () => subFiltersResult?.data?.List ?? [] as CollectionFilterType[], [subFiltersResult], ); diff --git a/src/components/Dialogs/ImportFolderModal.tsx b/src/components/Dialogs/ImportFolderModal.tsx index 371c22006..3e98376d9 100644 --- a/src/components/Dialogs/ImportFolderModal.tsx +++ b/src/components/Dialogs/ImportFolderModal.tsx @@ -16,6 +16,7 @@ import { } from '@/core/rtkQuery/splitV3Api/importFolderApi'; import { setStatus as setBrowseStatus } from '@/core/slices/modals/browseFolder'; import { setStatus } from '@/core/slices/modals/importFolder'; +import { isErrorWithMessage } from '@/core/util'; import BrowseFolderModal from './BrowseFolderModal'; @@ -53,7 +54,7 @@ function ImportFolderModal() { } }; - const handleInputChange = (event: any) => { + const handleInputChange = (event: React.ChangeEvent) => { const name = event.target.id; const value = name === 'WatchForNewFiles' ? event.target.value === '1' : event.target.value; setImportFolder({ ...importFolder, [name]: value }); @@ -62,28 +63,37 @@ function ImportFolderModal() { const handleBrowse = () => dispatch(setBrowseStatus(true)); const handleClose = () => dispatch(setStatus(false)); const handleDelete = async () => { - // TODO: can this be better typed? - const result: any = await deleteFolder({ folderId: ID }); - if (!result.error) { + try { + await deleteFolder({ folderId: ID }).unwrap(); toast.success('Import folder deleted!'); dispatch(setStatus(false)); + } catch (err) { + if (isErrorWithMessage(err)) { + console.error(err.message); + } } }; const handleSave = async () => { - // TODO: can this be better typed? - let result; if (edit) { - result = await updateFolder(importFolder); - if (!result.error) { + try { + await updateFolder(importFolder); toast.success('Import folder edited!'); dispatch(setStatus(false)); + } catch (err) { + if (isErrorWithMessage(err)) { + console.error(err.message); + } } } else { - result = await createFolder(importFolder); - if (!result.error) { + try { + await createFolder(importFolder); toast.success('Import folder added!'); dispatch(setStatus(false)); + } catch (err) { + if (isErrorWithMessage(err)) { + console.error(err.message); + } } } }; diff --git a/src/components/Dialogs/LanguagesModal.tsx b/src/components/Dialogs/LanguagesModal.tsx index 7d6095acd..558f68b45 100644 --- a/src/components/Dialogs/LanguagesModal.tsx +++ b/src/components/Dialogs/LanguagesModal.tsx @@ -76,7 +76,7 @@ function LanguagesModal({ onClose, type }: Props) { ); const [patchSettings] = usePatchSettingsMutation(); - const [languages, setLanguages] = useState([] as Array); + const [languages, setLanguages] = useState([] as string[]); const handleSave = useCallback(() => { patchSettings({ @@ -94,7 +94,7 @@ function LanguagesModal({ onClose, type }: Props) { if (type !== null) setLanguages(LanguagePreference); }, [type, LanguagePreference]); - const handleInputChange = (event: any) => { + const handleInputChange = (event) => { const { checked: value, id } = event.target; const newLanguages = languages.slice(); diff --git a/src/components/Dialogs/QueueModal.tsx b/src/components/Dialogs/QueueModal.tsx index 466fc6a16..d21f58c1d 100644 --- a/src/components/Dialogs/QueueModal.tsx +++ b/src/components/Dialogs/QueueModal.tsx @@ -116,7 +116,7 @@ const QueueModal = ({ onClose, show: showModal }: Props) => { }, 500), [getQuery]); const tabs = useMemo(() => - map(Object.keys(names) as Array, (key, index, { length }) => ( + map(Object.keys(names) as QueueName[], (key, index, { length }) => ( index !== length - 1 ? ( diff --git a/src/components/DnDList/DnDList.tsx b/src/components/DnDList/DnDList.tsx index b19102dd6..9e2f1026d 100644 --- a/src/components/DnDList/DnDList.tsx +++ b/src/components/DnDList/DnDList.tsx @@ -6,7 +6,7 @@ import PortalAwareItem from './PortalAwareItem'; type Props = { onDragEnd: (result: DropResult) => void; - children: Array<{ key: string, item: React.ReactNode }>; + children: { key: string, item: React.ReactNode }[]; }; function DnDList(props: Props) { diff --git a/src/components/Input/Button.tsx b/src/components/Input/Button.tsx index 097864177..83a70a10a 100644 --- a/src/components/Input/Button.tsx +++ b/src/components/Input/Button.tsx @@ -6,11 +6,11 @@ import cx from 'classnames'; type Props = { buttonType?: string; className?: string; - children: any; + children: React.ReactNode; disabled?: boolean; loading?: boolean; loadingSize?: number; - onClick?: (...args: any) => void; + onClick?: React.MouseEventHandler; submit?: boolean; tooltip?: string; }; diff --git a/src/components/Input/Checkbox.tsx b/src/components/Input/Checkbox.tsx index c1fa9bad2..8042b8037 100644 --- a/src/components/Input/Checkbox.tsx +++ b/src/components/Input/Checkbox.tsx @@ -13,7 +13,7 @@ type Props = { className?: string; labelRight?: boolean; justify?: boolean; - onChange: (event: any) => void; + onChange: React.ChangeEventHandler; }; function Checkbox({ className, id, intermediate, isChecked, justify, label, labelRight, onChange }: Props) { diff --git a/src/components/Input/Input.tsx b/src/components/Input/Input.tsx index b0747b04c..ab3b5f61a 100644 --- a/src/components/Input/Input.tsx +++ b/src/components/Input/Input.tsx @@ -8,15 +8,15 @@ type Props = { type: string; placeholder?: string; value: string | number; - onChange: (event: any) => void; - onKeyUp?: (event: any) => void; + onChange: React.ChangeEventHandler; + onKeyUp?: React.KeyboardEventHandler; className?: string; inputClassName?: string; autoFocus?: boolean; disabled?: boolean; center?: boolean; endIcon?: string; - endIconClick?: (event: any) => void; + endIconClick?: React.MouseEventHandler; startIcon?: string; inline?: boolean; }; diff --git a/src/components/Input/InputSmall.tsx b/src/components/Input/InputSmall.tsx index 7fa8288fa..bfcf0e5c9 100644 --- a/src/components/Input/InputSmall.tsx +++ b/src/components/Input/InputSmall.tsx @@ -5,8 +5,8 @@ type Props = { type: string; placeholder?: string; value: string | number; - onChange: (event: any) => void; - onKeyUp?: (event: any) => void; + onChange: React.ChangeEventHandler; + onKeyUp?: React.KeyboardEventHandler; className?: string; autoFocus?: boolean; disabled?: boolean; diff --git a/src/components/Input/Select.tsx b/src/components/Input/Select.tsx index 4cea660ce..099159ba7 100644 --- a/src/components/Input/Select.tsx +++ b/src/components/Input/Select.tsx @@ -5,9 +5,9 @@ import { Icon } from '@mdi/react'; type Props = { id: string; value: string | number; - onChange: (event: any) => void; + onChange: React.ChangeEventHandler; className?: string; - children: any; + children: React.ReactNode; label?: string; }; diff --git a/src/components/Input/SelectEpisodeList.tsx b/src/components/Input/SelectEpisodeList.tsx index 46a0920af..3a804489d 100644 --- a/src/components/Input/SelectEpisodeList.tsx +++ b/src/components/Input/SelectEpisodeList.tsx @@ -47,7 +47,7 @@ type Option = { }; type Props = { - options: Array