From cc0d111fa7c810a3b6dafcc025aa63b8a333b12b Mon Sep 17 00:00:00 2001 From: Robert Sun <107655677+rsun19@users.noreply.github.com> Date: Mon, 10 Jun 2024 13:33:58 -0400 Subject: [PATCH] first round of updates to disable type assertions --- frontend/.eslintrc | 12 ++ .../src/__tests__/unit/testUtils/hooks.ts | 7 +- frontend/src/api/errorUtils.ts | 3 +- frontend/src/api/modelRegistry/errorUtils.ts | 2 +- frontend/src/api/pipelines/errorUtils.ts | 6 +- frontend/src/api/prometheus/serving.ts | 24 ++-- frontend/src/api/trustyai/errorUtils.ts | 3 +- frontend/src/app/AppContext.ts | 10 +- frontend/src/components/DocCardBadges.tsx | 16 ++- frontend/src/components/OdhDocListItem.tsx | 29 ++-- frontend/src/components/ToastNotification.tsx | 3 +- .../browserStorage/BrowserStorageContext.tsx | 1 + .../components/table/useTableColumnSort.ts | 2 + frontend/src/concepts/areas/utils.ts | 1 + .../dashboard/DashboardSearchField.tsx | 3 +- .../concepts/distributedWorkloads/utils.tsx | 3 +- .../src/concepts/docResources/docUtils.ts | 1 + frontend/src/concepts/metrics/utils.ts | 6 +- .../context/ModelRegistryContext.tsx | 1 + .../content/PipelineAndVersionContext.tsx | 6 +- .../PipelineRunArtifactSelect.tsx | 6 +- .../ConfusionMatrixCompare.tsx | 4 +- .../metricsSection/confusionMatrix/utils.ts | 9 +- .../compareRuns/metricsSection/roc/utils.ts | 17 +-- .../compareRuns/metricsSection/utils.ts | 10 +- .../ConfigurePipelinesServerModal.tsx | 3 +- .../pipelines/content/createRun/RunPage.tsx | 2 +- .../content/tables/PipelineFilterBar.tsx | 4 +- .../content/tables/usePipelineFilter.ts | 8 +- .../pipelines/context/MlmdListContext.tsx | 1 + .../pipelines/context/PipelinesContext.tsx | 3 + frontend/src/concepts/pipelines/utils.ts | 1 + .../src/concepts/projects/ProjectsContext.tsx | 10 +- .../topology/PipelineDefaultTaskGroup.tsx | 1 + .../concepts/topology/PipelineTaskEdge.tsx | 1 + .../topology/customNodes/ArtifactTaskNode.tsx | 2 + .../topology/customNodes/StandardTaskNode.tsx | 1 + .../trustyai/context/TrustyAIContext.tsx | 1 + .../concepts/trustyai/useManageTrustyAICR.ts | 1 + .../AcceleratorIdentifierMultiselect.tsx | 6 +- .../pages/exploreApplication/EnableModal.tsx | 4 +- .../home/resources/useResourcesSection.tsx | 3 +- .../home/resources/useSpecifiedResources.tsx | 4 +- .../learningCenter/ApplicationFilters.tsx | 1 + .../pages/learningCenter/DocTypeFilters.tsx | 5 +- .../pages/learningCenter/EnabledFilters.tsx | 9 +- .../learningCenter/LearningCenterToolbar.tsx | 4 +- .../learningCenter/ProviderTypeFilters.tsx | 30 ++-- .../ModelVersionSelector.tsx | 6 +- .../ModelVersions/ModelVersionListView.tsx | 3 +- .../ModelVersionsArchiveListView.tsx | 3 +- .../RegisteredModelListView.tsx | 3 +- .../RegisteredModelsArchiveListView.tsx | 6 +- ...ustomServingRuntimeAPIProtocolSelector.tsx | 8 +- .../customServingRuntimes/utils.ts | 16 ++- .../modelServing/screens/global/utils.ts | 5 +- .../metrics/EnsureMetricsAvailable.tsx | 5 +- .../screens/metrics/bias/BiasChart.tsx | 47 +++--- .../MetricTypeField.tsx | 12 +- .../metrics/performance/ModelMeshMetrics.tsx | 43 +++--- .../metrics/performance/ServerGraphs.tsx | 136 +++++++++--------- .../modelServing/screens/metrics/utils.tsx | 4 +- .../screens/admin/NotebookControllerTabs.tsx | 6 +- .../screens/admin/UserTableCellTransform.tsx | 1 + .../server/EnvironmentVariablesField.tsx | 3 +- .../server/EnvironmentVariablesRow.tsx | 4 +- .../executions/details/ExecutionDetails.tsx | 4 +- .../global/runs/GlobalPipelineRunsTabs.tsx | 7 +- .../pages/projects/ProjectDetailsContext.tsx | 1 + .../deployedModels/DeployedModelsSection.tsx | 13 +- .../environmentVariables/EnvConfigMap.tsx | 5 +- .../environmentVariables/EnvSecret.tsx | 5 +- .../EnvTypeSelectField.tsx | 12 +- .../spawner/environmentVariables/utils.ts | 8 +- .../projects/screens/spawner/spawnerUtils.ts | 5 +- .../screens/spawner/useBuildStatuses.ts | 4 +- frontend/src/redux/context.ts | 1 + frontend/src/redux/store/store.ts | 4 +- frontend/src/typeHelpers.ts | 1 + frontend/src/utilities/NavData.tsx | 2 + .../utilities/useAcceleratorProfileState.ts | 6 +- frontend/src/utilities/useDraggableTable.ts | 3 +- .../utilities/useWatchNotebooksForUsers.tsx | 11 +- frontend/src/utilities/utils.ts | 49 ++++++- 84 files changed, 473 insertions(+), 269 deletions(-) diff --git a/frontend/.eslintrc b/frontend/.eslintrc index cca83da5be..ec95917b9b 100755 --- a/frontend/.eslintrc +++ b/frontend/.eslintrc @@ -315,6 +315,18 @@ } ] } + }, + { + "files": ["*.ts", "*.tsx"], + "excludedFiles": ["**/__mocks__/**", "**/__tests__/**"], + "rules": { + "@typescript-eslint/consistent-type-assertions": [ + "error", + { + "assertionStyle": "never" + } + ] + } } ] } diff --git a/frontend/src/__tests__/unit/testUtils/hooks.ts b/frontend/src/__tests__/unit/testUtils/hooks.ts index c82a2ff7cd..86c308422f 100644 --- a/frontend/src/__tests__/unit/testUtils/hooks.ts +++ b/frontend/src/__tests__/unit/testUtils/hooks.ts @@ -67,8 +67,8 @@ export const renderHook = < options?: RenderHookOptions, ): RenderHookResultExt => { let updateCount = 0; - let prevResult: Result | undefined; - let currentResult: Result | undefined; + let prevResult: Result; + let currentResult: Result; const renderResult = renderHookRTL((props) => { updateCount++; @@ -80,8 +80,7 @@ export const renderHook = < const renderResultExt: RenderHookResultExt = { ...renderResult, - getPreviousResult: () => - updateCount > 1 ? (prevResult as Result) : renderResult.result.current, + getPreviousResult: () => (updateCount > 1 ? prevResult : renderResult.result.current), getUpdateCount: () => updateCount, diff --git a/frontend/src/api/errorUtils.ts b/frontend/src/api/errorUtils.ts index e972b16c41..1ac6f1c5e9 100644 --- a/frontend/src/api/errorUtils.ts +++ b/frontend/src/api/errorUtils.ts @@ -2,7 +2,7 @@ import { K8sStatus } from '@openshift/dynamic-plugin-sdk-utils'; import { AxiosError } from 'axios'; export const isK8sStatus = (data: unknown): data is K8sStatus => - (data as K8sStatus).kind === 'Status'; + typeof data === 'object' && data !== null && 'kind' in data && data.kind === 'Status'; export class K8sStatusError extends Error { public statusObject: K8sStatus; @@ -18,6 +18,7 @@ const isAxiosErrorWithResponseMessage = ( error?: Error | AxiosError, ): error is AxiosError<{ message: string }> => Boolean( + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions error && typeof (error as AxiosError<{ message: string }>).response?.data.message === 'string', ); diff --git a/frontend/src/api/modelRegistry/errorUtils.ts b/frontend/src/api/modelRegistry/errorUtils.ts index d9a3238d74..1282802199 100644 --- a/frontend/src/api/modelRegistry/errorUtils.ts +++ b/frontend/src/api/modelRegistry/errorUtils.ts @@ -2,7 +2,7 @@ import { ModelRegistryError } from '~/concepts/modelRegistry/types'; import { isCommonStateError } from '~/utilities/useFetchState'; const isError = (e: unknown): e is ModelRegistryError => - ['code', 'message'].every((key) => key in (e as ModelRegistryError)); + typeof e === 'object' && e !== null && ['code', 'message'].every((key) => key in e); export const handleModelRegistryFailures = (promise: Promise): Promise => promise diff --git a/frontend/src/api/pipelines/errorUtils.ts b/frontend/src/api/pipelines/errorUtils.ts index 384e3d6d2f..4efaa5da7c 100644 --- a/frontend/src/api/pipelines/errorUtils.ts +++ b/frontend/src/api/pipelines/errorUtils.ts @@ -14,10 +14,12 @@ type ResultErrorKF = { }; const isErrorKF = (e: unknown): e is ErrorKF => - ['error', 'code', 'message'].every((key) => key in (e as ErrorKF)); + typeof e === 'object' && e !== null && ['error', 'code', 'message'].every((key) => key in e); const isErrorDetailsKF = (result: unknown): result is ResultErrorKF => - ['error_details', 'error_message'].every((key) => key in (result as ResultErrorKF)); + typeof result === 'object' && + result !== null && + ['error_details', 'error_message'].every((key) => key in result); export const handlePipelineFailures = (promise: Promise): Promise => promise diff --git a/frontend/src/api/prometheus/serving.ts b/frontend/src/api/prometheus/serving.ts index 9cf7f35188..749634b11a 100644 --- a/frontend/src/api/prometheus/serving.ts +++ b/frontend/src/api/prometheus/serving.ts @@ -43,7 +43,7 @@ export const useModelServingMetrics = ( const serverRequestCount = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.SERVER, - (queries as { [key in ServerMetricType]: string })[ServerMetricType.REQUEST_COUNT], + ServerMetricType.REQUEST_COUNT in queries ? queries[ServerMetricType.REQUEST_COUNT] : '', end, timeframe, defaultResponsePredicate, @@ -53,7 +53,9 @@ export const useModelServingMetrics = ( const serverAverageResponseTime = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.SERVER, - (queries as { [key in ServerMetricType]: string })[ServerMetricType.AVG_RESPONSE_TIME], + ServerMetricType.AVG_RESPONSE_TIME in queries + ? queries[ServerMetricType.AVG_RESPONSE_TIME] + : '', end, timeframe, prometheusQueryRangeResponsePredicate, @@ -62,7 +64,7 @@ export const useModelServingMetrics = ( const serverCPUUtilization = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.SERVER, - (queries as { [key in ServerMetricType]: string })[ServerMetricType.CPU_UTILIZATION], + ServerMetricType.CPU_UTILIZATION in queries ? queries[ServerMetricType.CPU_UTILIZATION] : '', end, timeframe, defaultResponsePredicate, @@ -71,7 +73,9 @@ export const useModelServingMetrics = ( const serverMemoryUtilization = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.SERVER, - (queries as { [key in ServerMetricType]: string })[ServerMetricType.MEMORY_UTILIZATION], + ServerMetricType.MEMORY_UTILIZATION in queries + ? queries[ServerMetricType.MEMORY_UTILIZATION] + : '', end, timeframe, defaultResponsePredicate, @@ -80,7 +84,9 @@ export const useModelServingMetrics = ( const modelRequestSuccessCount = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.MODEL, - (queries as { [key in ModelMetricType]: string })[ModelMetricType.REQUEST_COUNT_SUCCESS], + ModelMetricType.REQUEST_COUNT_SUCCESS in queries + ? queries[ModelMetricType.REQUEST_COUNT_SUCCESS] + : '', end, timeframe, defaultResponsePredicate, @@ -89,7 +95,9 @@ export const useModelServingMetrics = ( const modelRequestFailedCount = useQueryRangeResourceData( performanceMetricsAreaAvailable && type === PerformanceMetricType.MODEL, - (queries as { [key in ModelMetricType]: string })[ModelMetricType.REQUEST_COUNT_FAILED], + ModelMetricType.REQUEST_COUNT_FAILED in queries + ? queries[ModelMetricType.REQUEST_COUNT_FAILED] + : '', end, timeframe, defaultResponsePredicate, @@ -98,7 +106,7 @@ export const useModelServingMetrics = ( const modelTrustyAISPD = useQueryRangeResourceData( biasMetricsAreaAvailable && type === PerformanceMetricType.MODEL, - (queries as { [key in ModelMetricType]: string })[ModelMetricType.TRUSTY_AI_SPD], + ModelMetricType.TRUSTY_AI_SPD in queries ? queries[ModelMetricType.TRUSTY_AI_SPD] : '', end, timeframe, prometheusQueryRangeResponsePredicate, @@ -108,7 +116,7 @@ export const useModelServingMetrics = ( const modelTrustyAIDIR = useQueryRangeResourceData( biasMetricsAreaAvailable && type === PerformanceMetricType.MODEL, - (queries as { [key in ModelMetricType]: string })[ModelMetricType.TRUSTY_AI_DIR], + ModelMetricType.TRUSTY_AI_DIR in queries ? queries[ModelMetricType.TRUSTY_AI_DIR] : '', end, timeframe, prometheusQueryRangeResponsePredicate, diff --git a/frontend/src/api/trustyai/errorUtils.ts b/frontend/src/api/trustyai/errorUtils.ts index 60fd07a7f6..6f3625f38e 100644 --- a/frontend/src/api/trustyai/errorUtils.ts +++ b/frontend/src/api/trustyai/errorUtils.ts @@ -9,8 +9,7 @@ type TrustyAIClientErrorViolation = { }; const isTrustyAIClientError = (e: unknown): e is TrustyAIClientError => - typeof e === 'object' && - ['title', 'status', 'violations'].every((key) => key in (e as TrustyAIClientError)); + typeof e === 'object' && e !== null && ['title', 'status', 'violations'].every((key) => key in e); export const handleTrustyAIFailures = (promise: Promise): Promise => promise.then((result) => { diff --git a/frontend/src/app/AppContext.ts b/frontend/src/app/AppContext.ts index bdc0141450..2b51e99c15 100644 --- a/frontend/src/app/AppContext.ts +++ b/frontend/src/app/AppContext.ts @@ -8,13 +8,7 @@ type AppContextProps = { storageClasses: StorageClassKind[]; }; -const defaultAppContext: AppContextProps = { - buildStatuses: [], - // At runtime dashboardConfig is never null -- DO NOT DO THIS usually - dashboardConfig: null as unknown as DashboardConfigKind, - storageClasses: [] as StorageClassKind[], -}; - -export const AppContext = React.createContext(defaultAppContext); +// eslint-disable-next-line @typescript-eslint/consistent-type-assertions +export const AppContext = React.createContext({} as AppContextProps); export const useAppContext = (): AppContextProps => React.useContext(AppContext); diff --git a/frontend/src/components/DocCardBadges.tsx b/frontend/src/components/DocCardBadges.tsx index 9a82f54235..085d7cd56d 100644 --- a/frontend/src/components/DocCardBadges.tsx +++ b/frontend/src/components/DocCardBadges.tsx @@ -5,7 +5,7 @@ import { QuickStartContext, QuickStartContextValues } from '@patternfly/quicksta import { OdhDocument, OdhDocumentType } from '~/types'; import { getQuickStartCompletionStatus, CompletionStatusEnum } from '~/utilities/quickStartUtils'; import { DOC_TYPE_TOOLTIPS } from '~/utilities/const'; -import { getLabelColorForDocType, getDuration } from '~/utilities/utils'; +import { getLabelColorForDocType, getDuration, asEnumMember } from '~/utilities/utils'; import { DOC_TYPE_LABEL } from '~/pages/learningCenter/const'; import './OdhCard.scss'; @@ -19,7 +19,7 @@ const DocCardBadges: React.FC = ({ odhDoc }) => { const [completionStatus, setCompletionStatus] = React.useState< CompletionStatusEnum | undefined >(); - const docType = odhDoc.spec.type as OdhDocumentType; + const docType = asEnumMember(odhDoc.spec.type, OdhDocumentType); const docName = odhDoc.metadata.name; const duration = odhDoc.spec.durationMinutes; @@ -32,13 +32,15 @@ const DocCardBadges: React.FC = ({ odhDoc }) => { } }, [qsContext, docType, docName]); - const label = DOC_TYPE_LABEL[docType] || 'Documentation'; + const label = docType !== null ? DOC_TYPE_LABEL[docType] || 'Documentation' : null; return ( - - - + {docType && ( + + + + )} {duration ? ( ); + + return null; }; export default DocCardBadges; diff --git a/frontend/src/components/OdhDocListItem.tsx b/frontend/src/components/OdhDocListItem.tsx index 61414f2916..8386b2db30 100644 --- a/frontend/src/components/OdhDocListItem.tsx +++ b/frontend/src/components/OdhDocListItem.tsx @@ -5,7 +5,7 @@ import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import { OdhDocument, OdhDocumentType } from '~/types'; import { getQuickStartLabel, launchQuickStart } from '~/utilities/quickStartUtils'; import { DOC_TYPE_TOOLTIPS } from '~/utilities/const'; -import { getDuration } from '~/utilities/utils'; +import { asEnumMember, getDuration } from '~/utilities/utils'; import { useQuickStartCardSelected } from './useQuickStartCardSelected'; import FavoriteButton from './FavoriteButton'; @@ -26,18 +26,21 @@ const OdhDocListItem: React.FC = ({ odhDoc, favorite, updateFav }; const renderTypeBadge = () => { - const docType = odhDoc.spec.type as OdhDocumentType; - const typeBadgeClasses = classNames('odh-list-item__partner-badge odh-m-doc', { - 'odh-m-documentation': docType === OdhDocumentType.Documentation, - 'odh-m-tutorial': docType === OdhDocumentType.Tutorial, - 'odh-m-quick-start': docType === OdhDocumentType.QuickStart, - 'odh-m-how-to': docType === OdhDocumentType.HowTo, - }); - return ( - -
{odhDoc.spec.type}
-
- ); + const docType = asEnumMember(odhDoc.spec.type, OdhDocumentType); + if (docType !== null) { + const typeBadgeClasses = classNames('odh-list-item__partner-badge odh-m-doc', { + 'odh-m-documentation': docType === OdhDocumentType.Documentation, + 'odh-m-tutorial': docType === OdhDocumentType.Tutorial, + 'odh-m-quick-start': docType === OdhDocumentType.QuickStart, + 'odh-m-how-to': docType === OdhDocumentType.HowTo, + }); + return ( + +
{odhDoc.spec.type}
+
+ ); + } + return null; }; const renderDocLink = () => { diff --git a/frontend/src/components/ToastNotification.tsx b/frontend/src/components/ToastNotification.tsx index 4f2a2299ab..b0a945705b 100644 --- a/frontend/src/components/ToastNotification.tsx +++ b/frontend/src/components/ToastNotification.tsx @@ -3,6 +3,7 @@ import { Alert, AlertActionCloseButton, AlertVariant } from '@patternfly/react-c import { AppNotification } from '~/redux/types'; import { ackNotification, hideNotification } from '~/redux/actions/actions'; import { useAppDispatch } from '~/redux/hooks'; +import { asEnumMember } from '~/utilities/utils'; const TOAST_NOTIFICATION_TIMEOUT = 8 * 1000; @@ -36,7 +37,7 @@ const ToastNotification: React.FC = ({ notification }) = return ( dispatch(ackNotification(notification))} /> diff --git a/frontend/src/components/browserStorage/BrowserStorageContext.tsx b/frontend/src/components/browserStorage/BrowserStorageContext.tsx index f64d105113..ff903a9cfa 100644 --- a/frontend/src/components/browserStorage/BrowserStorageContext.tsx +++ b/frontend/src/components/browserStorage/BrowserStorageContext.tsx @@ -48,6 +48,7 @@ export const useBrowserStorage = ( [isSessionStorage, jsonify, setJSONValue, setStringValue, storageKey], ); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return [(getValue(storageKey, jsonify, isSessionStorage) as T) ?? defaultValue, setValue]; }; diff --git a/frontend/src/components/table/useTableColumnSort.ts b/frontend/src/components/table/useTableColumnSort.ts index ce179df0a0..56c4519d68 100644 --- a/frontend/src/components/table/useTableColumnSort.ts +++ b/frontend/src/components/table/useTableColumnSort.ts @@ -105,7 +105,9 @@ const useTableColumnSort = ( return 0; } + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const dataValueA = a[columnField.field as keyof T]; + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const dataValueB = b[columnField.field as keyof T]; if (typeof dataValueA === 'string' && typeof dataValueB === 'string') { return dataValueA.localeCompare(dataValueB); diff --git a/frontend/src/concepts/areas/utils.ts b/frontend/src/concepts/areas/utils.ts index 2957080141..db69662b9a 100644 --- a/frontend/src/concepts/areas/utils.ts +++ b/frontend/src/concepts/areas/utils.ts @@ -16,6 +16,7 @@ const getFlags = (dashboardConfigSpec: DashboardConfigKind['spec']): FlagState = return { ...Object.keys(flags).reduce((acc, key) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const value = flags[key as FeatureFlag]; if (isFeatureFlag(key, value)) { acc[key] = key.startsWith('disable') ? !value : value; diff --git a/frontend/src/concepts/dashboard/DashboardSearchField.tsx b/frontend/src/concepts/dashboard/DashboardSearchField.tsx index 18d45e1da6..f8c0a88051 100644 --- a/frontend/src/concepts/dashboard/DashboardSearchField.tsx +++ b/frontend/src/concepts/dashboard/DashboardSearchField.tsx @@ -1,6 +1,7 @@ import * as React from 'react'; import { InputGroup, SearchInput, InputGroupItem } from '@patternfly/react-core'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; +import { asEnumMember } from '~/utilities/utils'; // List all the possible search fields here export enum SearchType { @@ -48,7 +49,7 @@ const DashboardSearchField: React.FC = ({ }))} value={searchType} onChange={(key) => { - onSearchTypeChange(key as SearchType); + onSearchTypeChange(asEnumMember(key, SearchType)!); }} icon={icon} /> diff --git a/frontend/src/concepts/distributedWorkloads/utils.tsx b/frontend/src/concepts/distributedWorkloads/utils.tsx index db466618d7..29a4de3785 100644 --- a/frontend/src/concepts/distributedWorkloads/utils.tsx +++ b/frontend/src/concepts/distributedWorkloads/utils.tsx @@ -34,6 +34,7 @@ import { convertToUnit, } from '~/utilities/valueUnits'; import { WorkloadWithUsage } from '~/api'; +import { isEnumMember } from '~/utilities/utils'; export enum WorkloadStatusType { Pending = 'Pending', @@ -166,7 +167,7 @@ export const getWorkloadName = (workload: WorkloadKind): string => workload.metadata?.name || 'Unnamed'; export const isKnownWorkloadOwnerType = (s: string): s is WorkloadOwnerType => - (Object.values(WorkloadOwnerType) as string[]).includes(s); + isEnumMember(s, WorkloadOwnerType); export const getWorkloadOwner = ( workload: WorkloadKind, diff --git a/frontend/src/concepts/docResources/docUtils.ts b/frontend/src/concepts/docResources/docUtils.ts index e66d889d94..1abeb77360 100644 --- a/frontend/src/concepts/docResources/docUtils.ts +++ b/frontend/src/concepts/docResources/docUtils.ts @@ -30,6 +30,7 @@ export const getQuickStartDocs = ( // Get doc cards for the quick starts const docs: OdhDocument[] = quickStarts.map( (quickStart) => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions _.merge({}, quickStart, { spec: { type: OdhDocumentType.QuickStart, diff --git a/frontend/src/concepts/metrics/utils.ts b/frontend/src/concepts/metrics/utils.ts index 1f44bc91cf..c88e5d2592 100644 --- a/frontend/src/concepts/metrics/utils.ts +++ b/frontend/src/concepts/metrics/utils.ts @@ -1,12 +1,12 @@ import { SelectOptionObject } from '@patternfly/react-core/deprecated'; import { TimeframeTitle, RefreshIntervalTitle } from '~/concepts/metrics/types'; +import { isEnumMember } from '~/utilities/utils'; export const isTimeframeTitle = ( timeframe: string | SelectOptionObject, -): timeframe is TimeframeTitle => - Object.values(TimeframeTitle).includes(timeframe as TimeframeTitle); +): timeframe is TimeframeTitle => isEnumMember(timeframe.toString(), TimeframeTitle); export const isRefreshIntervalTitle = ( refreshInterval: string | SelectOptionObject, ): refreshInterval is RefreshIntervalTitle => - Object.values(RefreshIntervalTitle).includes(refreshInterval as RefreshIntervalTitle); + isEnumMember(refreshInterval.toString(), RefreshIntervalTitle); diff --git a/frontend/src/concepts/modelRegistry/context/ModelRegistryContext.tsx b/frontend/src/concepts/modelRegistry/context/ModelRegistryContext.tsx index eb3bdce52d..dfde7ebcc8 100644 --- a/frontend/src/concepts/modelRegistry/context/ModelRegistryContext.tsx +++ b/frontend/src/concepts/modelRegistry/context/ModelRegistryContext.tsx @@ -28,6 +28,7 @@ export const ModelRegistryContext = React.createContext undefined, refreshState: async () => undefined, diff --git a/frontend/src/concepts/pipelines/content/PipelineAndVersionContext.tsx b/frontend/src/concepts/pipelines/content/PipelineAndVersionContext.tsx index eed1a71d94..f298573f56 100644 --- a/frontend/src/concepts/pipelines/content/PipelineAndVersionContext.tsx +++ b/frontend/src/concepts/pipelines/content/PipelineAndVersionContext.tsx @@ -77,10 +77,10 @@ const PipelineAndVersionContextProvider: React.FC ({ pipelines: selectedPipelines, - versions: (Object.values(selectedVersions) as SelectedVersion[]) + versions: Object.values(selectedVersions) .map((selectedVersion) => - selectedVersion.versions.map((version) => ({ - pipelineName: selectedVersion.pipelineName, + selectedVersion!.versions.map((version) => ({ + pipelineName: selectedVersion!.pipelineName, version, })), ) diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/PipelineRunArtifactSelect.tsx b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/PipelineRunArtifactSelect.tsx index 2b15849d79..4137e164cc 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/PipelineRunArtifactSelect.tsx +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/PipelineRunArtifactSelect.tsx @@ -49,10 +49,10 @@ export const PipelineRunArtifactSelect = ({ isOpen={isOpen} selected={selectedItemTitles} onSelect={(_event, value) => { - if (selectedItemTitles.includes(value as string)) { + if (typeof value === 'string' && selectedItemTitles.includes(value)) { setSelectedItemTitles(selectedItemTitles.filter((id) => id !== value)); - } else { - setSelectedItemTitles([...selectedItemTitles, value as string]); + } else if (typeof value === 'string') { + setSelectedItemTitles([...selectedItemTitles, value]); } }} onOpenChange={(nextOpen: boolean) => setIsOpen(nextOpen)} diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/ConfusionMatrixCompare.tsx b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/ConfusionMatrixCompare.tsx index 60ec951f71..c8205ed80c 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/ConfusionMatrixCompare.tsx +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/ConfusionMatrixCompare.tsx @@ -55,7 +55,7 @@ const ConfusionMatrixCompare: React.FC = ({ const data = customProperties.get('confusionMatrix')?.getStructValue()?.toJavaScript(); if (data) { - const confusionMatrixData = data.struct as unknown; + const confusionMatrixData: unknown = data.struct; if (isConfusionMatrix(confusionMatrixData)) { const runId = fullPath.run.run_id; const title = getFullArtifactPathLabel(fullPath); @@ -79,7 +79,9 @@ const ConfusionMatrixCompare: React.FC = ({ return acc; }, { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions runMap: {} as Record, + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions configMap: {} as Record, }, ), diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils.ts b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils.ts index 47fea29c76..2891587bd9 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils.ts +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/confusionMatrix/utils.ts @@ -1,8 +1,12 @@ import { ConfusionMatrixInput } from '~/concepts/pipelines/content/artifacts/charts/confusionMatrix/types'; export const isConfusionMatrix = (obj: unknown): obj is ConfusionMatrixInput => { - const matrix = obj as ConfusionMatrixInput; + const matrix = obj; return ( + typeof matrix === 'object' && + matrix !== null && + 'annotationSpecs' in matrix && + 'rows' in matrix && Array.isArray(matrix.annotationSpecs) && matrix.annotationSpecs.every( (annotationSpec) => @@ -10,7 +14,8 @@ export const isConfusionMatrix = (obj: unknown): obj is ConfusionMatrixInput => ) && Array.isArray(matrix.rows) && matrix.rows.every( - (row) => Array.isArray(row.row) && row.row.every((value) => typeof value === 'number'), + (row) => + Array.isArray(row.row) && row.row.every((value: unknown) => typeof value === 'number'), ) ); }; diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/roc/utils.ts b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/roc/utils.ts index c3bf9da5d8..74582cf10e 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/roc/utils.ts +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/roc/utils.ts @@ -2,14 +2,15 @@ import { JavaScriptValue } from 'google-protobuf/google/protobuf/struct_pb'; import { ROCCurveConfig } from '~/concepts/pipelines/content/artifacts/charts/ROCCurve'; import { ConfidenceMetric } from './types'; -export const isConfidenceMetric = (obj: JavaScriptValue): obj is ConfidenceMetric => { - const metric = obj as ConfidenceMetric; - return ( - typeof metric.confidenceThreshold === 'number' && - typeof metric.falsePositiveRate === 'number' && - typeof metric.recall === 'number' - ); -}; +export const isConfidenceMetric = (obj: JavaScriptValue): obj is ConfidenceMetric => + typeof obj === 'object' && + obj !== null && + 'confidenceThreshold' in obj && + 'falsePositiveRate' in obj && + 'recall' in obj && + typeof obj.confidenceThreshold === 'number' && + typeof obj.falsePositiveRate === 'number' && + typeof obj.recall === 'number'; export const buildRocCurveConfig = ( confidenceMetricsArray: ConfidenceMetric[], diff --git a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/utils.ts b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/utils.ts index 38a034f605..b5907e164e 100644 --- a/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/utils.ts +++ b/frontend/src/concepts/pipelines/content/compareRuns/metricsSection/utils.ts @@ -58,7 +58,7 @@ export const getRunArtifacts = (mlmdPackages: PipelineRunRelatedMlmd[]): RunArti linkedArtifacts.push({ event, artifact, - } as LinkedArtifact); + }); } else { throw new Error(`The artifact with the following ID was not found: ${artifactId}`); } @@ -66,12 +66,12 @@ export const getRunArtifacts = (mlmdPackages: PipelineRunRelatedMlmd[]): RunArti return { execution, linkedArtifacts, - } as ExecutionArtifact; + }; }); return { run: mlmdPackage.run, executionArtifacts, - } as RunArtifact; + }; }); export const filterRunArtifactsByType = ( @@ -102,14 +102,14 @@ export const filterRunArtifactsByType = ( typeExecutions.push({ execution: e.execution, linkedArtifacts: typeArtifacts, - } as ExecutionArtifact); + }); } } if (typeExecutions.length > 0) { typeRuns.push({ run: runArtifact.run, executionArtifacts: typeExecutions, - } as RunArtifact); + }); } } return typeRuns; diff --git a/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx b/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx index 252de75602..ccb6e20158 100644 --- a/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx +++ b/frontend/src/concepts/pipelines/content/configurePipelinesServer/ConfigurePipelinesServerModal.tsx @@ -6,6 +6,7 @@ import { createPipelinesCR, deleteSecret } from '~/api'; import useDataConnections from '~/pages/projects/screens/detail/data-connections/useDataConnections'; import { EMPTY_AWS_PIPELINE_DATA } from '~/pages/projects/dataConnections/const'; import DashboardModalFooter from '~/concepts/dashboard/DashboardModalFooter'; +import { asEnumMember } from '~/utilities/utils'; import { PipelinesDatabaseSection } from './PipelinesDatabaseSection'; import { ObjectStorageSection } from './ObjectStorageSection'; import { @@ -48,7 +49,7 @@ export const ConfigurePipelinesServerModal: React.FC DATABASE_CONNECTION_FIELDS.filter((field) => field.isRequired) .map((field) => field.key) - .includes(key as DatabaseConnectionKeys) + .includes(asEnumMember(key, DatabaseConnectionKeys)!) ? !!value : true, ); diff --git a/frontend/src/concepts/pipelines/content/createRun/RunPage.tsx b/frontend/src/concepts/pipelines/content/createRun/RunPage.tsx index 8761f6f942..8a62b397d9 100644 --- a/frontend/src/concepts/pipelines/content/createRun/RunPage.tsx +++ b/frontend/src/concepts/pipelines/content/createRun/RunPage.tsx @@ -118,7 +118,7 @@ const RunPage: React.FC = ({ cloneRun, contextPath, testId }) => { > diff --git a/frontend/src/concepts/pipelines/content/tables/PipelineFilterBar.tsx b/frontend/src/concepts/pipelines/content/tables/PipelineFilterBar.tsx index 16bdea92fe..63c4148272 100644 --- a/frontend/src/concepts/pipelines/content/tables/PipelineFilterBar.tsx +++ b/frontend/src/concepts/pipelines/content/tables/PipelineFilterBar.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/consistent-type-assertions */ import * as React from 'react'; import { ToolbarFilter, @@ -41,11 +42,12 @@ export function FilterToolbar({ testId = 'filter-toolbar', ...toolbarGroupProps }: ToolbarFilterProps): React.JSX.Element { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const keys = Object.keys(filterOptions) as Array; const [open, setOpen] = React.useState(false); const [currentFilterType, setCurrentFilterType] = React.useState(keys[0]); const isToolbarChip = (v: unknown): v is ToolbarChip & { key: T } => - !!v && Object.keys(v as ToolbarChip).every((k) => ['key', 'node'].includes(k)); + typeof v === 'object' && v !== null && Object.keys(v).every((k) => ['key', 'node'].includes(k)); return ( <> diff --git a/frontend/src/concepts/pipelines/content/tables/usePipelineFilter.ts b/frontend/src/concepts/pipelines/content/tables/usePipelineFilter.ts index 9a1cd1031d..70235f7631 100644 --- a/frontend/src/concepts/pipelines/content/tables/usePipelineFilter.ts +++ b/frontend/src/concepts/pipelines/content/tables/usePipelineFilter.ts @@ -14,13 +14,11 @@ export enum FilterOptions { PIPELINE_VERSION = 'pipeline_version', } -export const getDataValue = ( - data: R, -): string | undefined => { - if (typeof data === 'string') { +export const getDataValue = (data: string | { value: string } | undefined): string | undefined => { + if (typeof data === 'string' || typeof data === 'undefined') { return data; } - return (data as { label: string; value: string } | undefined)?.value; + return data.value; }; const defaultFilterData: FilterProps['filterData'] = { diff --git a/frontend/src/concepts/pipelines/context/MlmdListContext.tsx b/frontend/src/concepts/pipelines/context/MlmdListContext.tsx index 159e7ff51a..4ddeb44524 100644 --- a/frontend/src/concepts/pipelines/context/MlmdListContext.tsx +++ b/frontend/src/concepts/pipelines/context/MlmdListContext.tsx @@ -16,6 +16,7 @@ export interface MlmdListContextProps { setOrderBy: (orderBy: MlmdOrderBy | undefined) => void; } +// eslint-disable-next-line @typescript-eslint/consistent-type-assertions const MlmdListContext = React.createContext({} as MlmdListContextProps); export const MlmdListContextProvider: React.FC = ({ children }) => { diff --git a/frontend/src/concepts/pipelines/context/PipelinesContext.tsx b/frontend/src/concepts/pipelines/context/PipelinesContext.tsx index 83af9ea8f0..539b340502 100644 --- a/frontend/src/concepts/pipelines/context/PipelinesContext.tsx +++ b/frontend/src/concepts/pipelines/context/PipelinesContext.tsx @@ -50,6 +50,7 @@ const PipelinesContext = React.createContext({ serverTimedOut: false, ignoreTimedOut: () => undefined, namespace: '', + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions project: null as unknown as ProjectKind, refreshState: async () => undefined, refreshAPIState: () => undefined, @@ -57,7 +58,9 @@ const PipelinesContext = React.createContext({ loading: false, data: null, }), + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions apiState: { apiAvailable: false, api: null as unknown as PipelineAPIState['api'] }, + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions metadataStoreServiceClient: null as unknown as MetadataStoreServicePromiseClient, }); diff --git a/frontend/src/concepts/pipelines/utils.ts b/frontend/src/concepts/pipelines/utils.ts index 006bb883f9..aec064f3df 100644 --- a/frontend/src/concepts/pipelines/utils.ts +++ b/frontend/src/concepts/pipelines/utils.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/consistent-type-assertions */ import { deletePipelineCR, deleteSecret, getPipelinesCR } from '~/api'; import { DSPA_SECRET_NAME, diff --git a/frontend/src/concepts/projects/ProjectsContext.tsx b/frontend/src/concepts/projects/ProjectsContext.tsx index dce7d107cc..97ed7b2265 100644 --- a/frontend/src/concepts/projects/ProjectsContext.tsx +++ b/frontend/src/concepts/projects/ProjectsContext.tsx @@ -50,11 +50,19 @@ type ProjectsProviderProps = { children: React.ReactNode; }; +const isError = (obj: unknown): obj is Error => + typeof obj === 'object' && + obj !== null && + 'name' in obj && + 'message' in obj && + typeof obj.name === 'string' && + typeof obj.message === 'string'; + const ProjectsContextProvider: React.FC = ({ children }) => { const [preferredProject, setPreferredProject] = React.useState(null); const [projectData, loaded, error] = useProjects(); - const loadError = error as Error | undefined; + const loadError = isError(error) ? error : undefined; const { dashboardNamespace } = useDashboardNamespace(); const { projects, dataScienceProjects, modelServingProjects, nonActiveProjects } = React.useMemo( diff --git a/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx b/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx index 520ad656da..6c7c79f900 100644 --- a/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx +++ b/frontend/src/concepts/topology/PipelineDefaultTaskGroup.tsx @@ -80,6 +80,7 @@ const DefaultTaskGroupInner: React.FunctionComponent + {/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions*/} }> {element.isCollapsed() ? ( = ({ element, ...props }) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const edge = element as Edge; return ( = observer( return ( } > {isHover || detailsLevel !== ScaleDetailsLevel.high ? ( @@ -159,6 +160,7 @@ type ArtifactTaskNodeProps = { } & WithSelectionProps; const ArtifactTaskNode: React.FC = ({ element, ...rest }) => ( + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions ); diff --git a/frontend/src/concepts/topology/customNodes/StandardTaskNode.tsx b/frontend/src/concepts/topology/customNodes/StandardTaskNode.tsx index 9f0eeba370..a2218fe30f 100644 --- a/frontend/src/concepts/topology/customNodes/StandardTaskNode.tsx +++ b/frontend/src/concepts/topology/customNodes/StandardTaskNode.tsx @@ -38,6 +38,7 @@ const StandardTaskNode: React.FunctionComponent = ({ ) : null; return ( + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions }> ({ data: DEFAULT_CONTEXT_DATA, refreshState: async () => undefined, refreshAPIState: () => undefined, + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions apiState: { apiAvailable: false, api: null as unknown as TrustyAPIState['api'] }, }); diff --git a/frontend/src/concepts/trustyai/useManageTrustyAICR.ts b/frontend/src/concepts/trustyai/useManageTrustyAICR.ts index f75e70e739..2f0631cecc 100644 --- a/frontend/src/concepts/trustyai/useManageTrustyAICR.ts +++ b/frontend/src/concepts/trustyai/useManageTrustyAICR.ts @@ -39,6 +39,7 @@ const useManageTrustyAICR = (namespace: string): UseManageTrustyAICRReturnType = [namespace, refresh], ); + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions return { error, isProgressing, diff --git a/frontend/src/pages/BYONImages/BYONImageModal/AcceleratorIdentifierMultiselect.tsx b/frontend/src/pages/BYONImages/BYONImageModal/AcceleratorIdentifierMultiselect.tsx index a71eb628e4..5e646a6c60 100644 --- a/frontend/src/pages/BYONImages/BYONImageModal/AcceleratorIdentifierMultiselect.tsx +++ b/frontend/src/pages/BYONImages/BYONImageModal/AcceleratorIdentifierMultiselect.tsx @@ -170,7 +170,11 @@ export const AcceleratorIdentifierMultiselect: React.FC onSelect(selection as string)} + onSelect={(ev, selection) => { + if (typeof selection === 'string') { + onSelect(selection); + } + }} onOpenChange={() => setIsOpen(false)} toggle={toggle} > diff --git a/frontend/src/pages/exploreApplication/EnableModal.tsx b/frontend/src/pages/exploreApplication/EnableModal.tsx index 17cc480bb0..6496498126 100644 --- a/frontend/src/pages/exploreApplication/EnableModal.tsx +++ b/frontend/src/pages/exploreApplication/EnableModal.tsx @@ -12,8 +12,8 @@ import { import { ExternalLinkAltIcon } from '@patternfly/react-icons'; import { OdhApplication } from '~/types'; import { EnableApplicationStatus, useEnableApplication } from '~/utilities/useEnableApplication'; +import { asEnumMember } from '~/utilities/utils'; import EnableVariable from './EnableVariable'; - import './EnableModal.scss'; type EnableModalProps = { @@ -158,7 +158,7 @@ const EnableModal: React.FC = ({ selectedApp, shown, onClose } key={key} ref={index === 0 ? focusRef : undefined} label={enable.variableDisplayText?.[key] ?? ''} - inputType={enable.variables?.[key] as TextInputTypes} + inputType={asEnumMember(enable.variables?.[key], TextInputTypes)!} helperText={enable.variableHelpText?.[key] ?? ''} validationInProgress={validationInProgress} value={enableValues[key]} diff --git a/frontend/src/pages/home/resources/useResourcesSection.tsx b/frontend/src/pages/home/resources/useResourcesSection.tsx index 4b78d05407..ab66e247a0 100644 --- a/frontend/src/pages/home/resources/useResourcesSection.tsx +++ b/frontend/src/pages/home/resources/useResourcesSection.tsx @@ -12,7 +12,6 @@ import { TextVariants, } from '@patternfly/react-core'; import { ExclamationCircleIcon } from '@patternfly/react-icons'; -import { OdhDocument } from '~/types'; import OdhDocCard from '~/components/OdhDocCard'; import ScrolledGallery from '~/concepts/design/ScrolledGallery'; import CollapsibleSection from '~/concepts/design/CollapsibleSection'; @@ -65,7 +64,7 @@ export const useResourcesSection = (): React.ReactNode => { { + const foundDocs = specifiedDocs.reduce((acc, included) => { const doc = docs.find((d) => d.metadata.name === included.name && d.kind === included.kind); if (doc) { acc.push(doc); } return acc; - }, [] as OdhDocument[]); + }, []); return { docs: foundDocs, loaded, loadError }; }, [docs, specifiedDocs, loadError, loaded]); }; diff --git a/frontend/src/pages/learningCenter/ApplicationFilters.tsx b/frontend/src/pages/learningCenter/ApplicationFilters.tsx index eeb3b7f88f..8b3de18486 100644 --- a/frontend/src/pages/learningCenter/ApplicationFilters.tsx +++ b/frontend/src/pages/learningCenter/ApplicationFilters.tsx @@ -35,6 +35,7 @@ const ApplicationFilters: React.FC = ({ docApps, catego }, [categoryApps, docApps]); const onFilterChange = (docType: string, e: React.SyntheticEvent): void => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const { checked } = e.target as React.AllHTMLAttributes; const updatedQuery = [...providerFilters]; const index = updatedQuery.indexOf(docType); diff --git a/frontend/src/pages/learningCenter/DocTypeFilters.tsx b/frontend/src/pages/learningCenter/DocTypeFilters.tsx index 7b3d7c94ee..15ddc6321b 100644 --- a/frontend/src/pages/learningCenter/DocTypeFilters.tsx +++ b/frontend/src/pages/learningCenter/DocTypeFilters.tsx @@ -4,6 +4,7 @@ import { FilterSidePanelCategory } from '@patternfly/react-catalog-view-extensio import FilterSidePanelCategoryItem from '~/components/FilterSidePanelCategoryItem'; import { OdhDocument, OdhDocumentType } from '~/types'; import { removeQueryArgument, setQueryArgument } from '~/utilities/router'; +import { isHTMLInputElementChecked } from '~/utilities/utils'; import { DOC_TYPE_FILTER_KEY } from './const'; import { useQueryFilters } from './useQueryFilters'; @@ -19,6 +20,7 @@ const DocTypeFilters: React.FC = ({ categoryApps }) => { categoryApps.reduce( (acc, docApp) => { if (docApp.spec.type in acc) { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions acc[docApp.spec.type as keyof typeof acc]++; } return acc; @@ -53,6 +55,7 @@ const DocTypeFilters: React.FC = ({ categoryApps }) => { return ( {Object.keys(OdhDocumentType).map((docType) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const value = OdhDocumentType[docType as keyof typeof OdhDocumentType]; return ( = ({ categoryApps }) => { onClick={(e) => onFilterChange( value, - (e.target as React.AllHTMLAttributes).checked || false, + isHTMLInputElementChecked(e.target) ? e.target.checked || false : false, ) } title={docType} diff --git a/frontend/src/pages/learningCenter/EnabledFilters.tsx b/frontend/src/pages/learningCenter/EnabledFilters.tsx index de72e83ea7..97607e6202 100644 --- a/frontend/src/pages/learningCenter/EnabledFilters.tsx +++ b/frontend/src/pages/learningCenter/EnabledFilters.tsx @@ -4,6 +4,7 @@ import { FilterSidePanelCategory } from '@patternfly/react-catalog-view-extensio import FilterSidePanelCategoryItem from '~/components/FilterSidePanelCategoryItem'; import { OdhDocument } from '~/types'; import { removeQueryArgument, setQueryArgument } from '~/utilities/router'; +import { isHTMLInputElementChecked } from '~/utilities/utils'; import { ENABLED_FILTER_KEY } from './const'; import { useQueryFilters } from './useQueryFilters'; @@ -50,7 +51,9 @@ const EnabledFilter: React.FC = ({ categoryApps }) => { onClick={(e) => onFilterChange( 'true', - (e.target as React.AllHTMLAttributes).checked || false, + isHTMLInputElementChecked(e.target) && typeof e.target.checked !== 'undefined' + ? e.target.checked + : false, ) } title="Enabled" @@ -64,7 +67,9 @@ const EnabledFilter: React.FC = ({ categoryApps }) => { onClick={(e) => onFilterChange( 'false', - (e.target as React.AllHTMLAttributes).checked || false, + isHTMLInputElementChecked(e.target) && typeof e.target.checked !== 'undefined' + ? e.target.checked + : false, ) } title="Not enabled" diff --git a/frontend/src/pages/learningCenter/LearningCenterToolbar.tsx b/frontend/src/pages/learningCenter/LearningCenterToolbar.tsx index fda12ae799..bf10b8b391 100644 --- a/frontend/src/pages/learningCenter/LearningCenterToolbar.tsx +++ b/frontend/src/pages/learningCenter/LearningCenterToolbar.tsx @@ -113,7 +113,7 @@ const LearningCenterToolbar: React.FC = ({ const onSortTypeSelect = React.useCallback( (e: React.MouseEvent | React.ChangeEvent) => { setIsSortTypeDropdownOpen(false); - const selection = (e.target as Element).getAttribute('data-key') ?? ''; + const selection = e.target instanceof Element ? e.target.getAttribute('data-key') ?? '' : ''; setQueryArgument(navigate, DOC_SORT_KEY, selection); }, [navigate], @@ -133,7 +133,7 @@ const LearningCenterToolbar: React.FC = ({ const onSortOrderSelect = React.useCallback( (e: React.MouseEvent | React.ChangeEvent) => { setIsSortOrderDropdownOpen(false); - const selection = (e.target as Element).getAttribute('data-key') ?? ''; + const selection = e.target instanceof Element ? e.target.getAttribute('data-key') ?? '' : ''; setQueryArgument(navigate, DOC_SORT_ORDER_KEY, selection); }, [navigate], diff --git a/frontend/src/pages/learningCenter/ProviderTypeFilters.tsx b/frontend/src/pages/learningCenter/ProviderTypeFilters.tsx index ffb216ca36..4fd5269e6a 100644 --- a/frontend/src/pages/learningCenter/ProviderTypeFilters.tsx +++ b/frontend/src/pages/learningCenter/ProviderTypeFilters.tsx @@ -5,6 +5,7 @@ import { OdhDocument } from '~/types'; import FilterSidePanelCategoryItem from '~/components/FilterSidePanelCategoryItem'; import { removeQueryArgument, setQueryArgument } from '~/utilities/router'; import { ODH_PRODUCT_NAME } from '~/utilities/const'; +import { isHTMLInputElementChecked } from '~/utilities/utils'; import { PROVIDER_TYPE_FILTER_KEY } from './const'; import { useQueryFilters } from './useQueryFilters'; @@ -40,21 +41,24 @@ const ProviderTypeFilters: React.FC = ({ docApps, cate }, [categoryApps, docApps]); const onFilterChange = (docType: string, e: React.SyntheticEvent): void => { - const { checked } = e.target as React.AllHTMLAttributes; - const updatedQuery = [...providerTypeFilters]; - const index = updatedQuery.indexOf(docType); - if (checked && index === -1) { - updatedQuery.push(docType); - } - if (!checked && index !== -1) { - updatedQuery.splice(index, 1); - } + if (isHTMLInputElementChecked(e.target)) { + const { checked } = e.target; - if (!updatedQuery.length) { - removeQueryArgument(navigate, PROVIDER_TYPE_FILTER_KEY); - return; + const updatedQuery = [...providerTypeFilters]; + const index = updatedQuery.indexOf(docType); + if (checked && index === -1) { + updatedQuery.push(docType); + } + if (!checked && index !== -1) { + updatedQuery.splice(index, 1); + } + + if (!updatedQuery.length) { + removeQueryArgument(navigate, PROVIDER_TYPE_FILTER_KEY); + return; + } + setQueryArgument(navigate, PROVIDER_TYPE_FILTER_KEY, JSON.stringify(updatedQuery)); } - setQueryArgument(navigate, PROVIDER_TYPE_FILTER_KEY, JSON.stringify(updatedQuery)); }; if (!Object.keys(providerTypes).length) { diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionSelector.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionSelector.tsx index cf00d689ea..80aadb7cca 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionSelector.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionDetails/ModelVersionSelector.tsx @@ -53,8 +53,10 @@ const ModelVersionSelector: React.FC = ({ const menu = ( { - onSelect(itemId as string); - setOpen(false); + if (typeof itemId === 'string') { + onSelect(itemId); + setOpen(false); + } }} data-id="model-version-selector-menu" ref={menuRef} diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionListView.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionListView.tsx index 049454ed25..a6609394e1 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersions/ModelVersionListView.tsx @@ -22,6 +22,7 @@ import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/Em import { filterModelVersions } from '~/pages/modelRegistry/screens/utils'; import { ModelRegistrySelectorContext } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext'; import { modelVersionArchiveUrl } from '~/pages/modelRegistry/screens/routeUtils'; +import { asEnumMember } from '~/utilities/utils'; import ModelVersionsTable from './ModelVersionsTable'; type ModelVersionListViewProps = { @@ -89,7 +90,7 @@ const ModelVersionListView: React.FC = ({ }))} value={searchType} onChange={(newSearchType) => { - setSearchType(newSearchType as SearchType); + setSearchType(asEnumMember(newSearchType, SearchType)!); }} icon={} /> diff --git a/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx b/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx index e7bf2dcc50..3450942f69 100644 --- a/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx @@ -13,6 +13,7 @@ import { ModelVersion } from '~/concepts/modelRegistry/types'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; import { filterModelVersions } from '~/pages/modelRegistry/screens/utils'; import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/EmptyModelRegistryState'; +import { asEnumMember } from '~/utilities/utils'; import ModelVersionsArchiveTable from './ModelVersionsArchiveTable'; type ModelVersionsArchiveListViewProps = { @@ -63,7 +64,7 @@ const ModelVersionsArchiveListView: React.FC }))} value={searchType} onChange={(newSearchType) => { - setSearchType(newSearchType as SearchType); + setSearchType(asEnumMember(newSearchType, SearchType)!); }} icon={} /> diff --git a/frontend/src/pages/modelRegistry/screens/RegisteredModels/RegisteredModelListView.tsx b/frontend/src/pages/modelRegistry/screens/RegisteredModels/RegisteredModelListView.tsx index 561cee5da8..e6ce7d2eed 100644 --- a/frontend/src/pages/modelRegistry/screens/RegisteredModels/RegisteredModelListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/RegisteredModels/RegisteredModelListView.tsx @@ -9,6 +9,7 @@ import { filterRegisteredModels } from '~/pages/modelRegistry/screens/utils'; import { ModelRegistrySelectorContext } from '~/concepts/modelRegistry/context/ModelRegistrySelectorContext'; import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/EmptyModelRegistryState'; import { registeredModelArchiveUrl } from '~/pages/modelRegistry/screens/routeUtils'; +import { asEnumMember } from '~/utilities/utils'; import RegisteredModelTable from './RegisteredModelTable'; import RegisteredModelsTableToolbar from './RegisteredModelsTableToolbar'; @@ -71,7 +72,7 @@ const RegisteredModelListView: React.FC = ({ }))} value={searchType} onChange={(newSearchType) => { - setSearchType(newSearchType as SearchType); + setSearchType(asEnumMember(newSearchType, SearchType)!); }} icon={} /> diff --git a/frontend/src/pages/modelRegistry/screens/RegisteredModelsArchive/RegisteredModelsArchiveListView.tsx b/frontend/src/pages/modelRegistry/screens/RegisteredModelsArchive/RegisteredModelsArchiveListView.tsx index 7e5de0392c..86a0f2e9db 100644 --- a/frontend/src/pages/modelRegistry/screens/RegisteredModelsArchive/RegisteredModelsArchiveListView.tsx +++ b/frontend/src/pages/modelRegistry/screens/RegisteredModelsArchive/RegisteredModelsArchiveListView.tsx @@ -13,6 +13,7 @@ import { RegisteredModel } from '~/concepts/modelRegistry/types'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; import { filterRegisteredModels } from '~/pages/modelRegistry/screens/utils'; import EmptyModelRegistryState from '~/pages/modelRegistry/screens/components/EmptyModelRegistryState'; +import { asEnumMember } from '~/utilities/utils'; import RegisteredModelsArchiveTable from './RegisteredModelsArchiveTable'; type RegisteredModelsArchiveListViewProps = { @@ -68,7 +69,10 @@ const RegisteredModelsArchiveListView: React.FC { - setSearchType(newSearchType as SearchType); + const newSearchTypeInput = asEnumMember(newSearchType, SearchType); + if (newSearchTypeInput !== null) { + setSearchType(newSearchTypeInput); + } }} icon={} /> diff --git a/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAPIProtocolSelector.tsx b/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAPIProtocolSelector.tsx index 2b9d485279..b22321b07e 100644 --- a/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAPIProtocolSelector.tsx +++ b/frontend/src/pages/modelServing/customServingRuntimes/CustomServingRuntimeAPIProtocolSelector.tsx @@ -2,6 +2,7 @@ import * as React from 'react'; import { FormGroup } from '@patternfly/react-core'; import { ServingRuntimeAPIProtocol, ServingRuntimePlatform } from '~/types'; import SimpleDropdownSelect from '~/components/SimpleDropdownSelect'; +import { asEnumMember } from '~/utilities/utils'; type CustomServingRuntimeAPIProtocolSelectorProps = { selectedAPIProtocol: ServingRuntimeAPIProtocol | undefined; @@ -50,7 +51,12 @@ const CustomServingRuntimeAPIProtocolSelector: React.FC< isDisabled={isOnlyModelMesh} options={options} value={selectedAPIProtocol || ''} - onChange={(key) => setSelectedAPIProtocol(key as ServingRuntimeAPIProtocol)} + onChange={(key) => { + const enumValue = asEnumMember(key, ServingRuntimeAPIProtocol); + if (enumValue !== null) { + setSelectedAPIProtocol(enumValue); + } + }} /> ); diff --git a/frontend/src/pages/modelServing/customServingRuntimes/utils.ts b/frontend/src/pages/modelServing/customServingRuntimes/utils.ts index 21330644fc..6e0c34f66c 100644 --- a/frontend/src/pages/modelServing/customServingRuntimes/utils.ts +++ b/frontend/src/pages/modelServing/customServingRuntimes/utils.ts @@ -2,6 +2,7 @@ import { K8sResourceCommon } from '@openshift/dynamic-plugin-sdk-utils'; import { ServingRuntimeKind, TemplateKind } from '~/k8sTypes'; import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; import { ServingRuntimeAPIProtocol, ServingRuntimePlatform } from '~/types'; +import { asEnumMember } from '~/utilities/utils'; export const getTemplateEnabled = ( template: TemplateKind, @@ -142,8 +143,12 @@ export const getAPIProtocolFromTemplate = ( if (!template.metadata.annotations?.['opendatahub.io/apiProtocol']) { return undefined; } - - return template.metadata.annotations['opendatahub.io/apiProtocol'] as ServingRuntimeAPIProtocol; + return ( + asEnumMember( + template.metadata.annotations['opendatahub.io/apiProtocol'], + ServingRuntimeAPIProtocol, + ) ?? undefined + ); }; export const getAPIProtocolFromServingRuntime = ( @@ -152,5 +157,10 @@ export const getAPIProtocolFromServingRuntime = ( if (!resource.metadata.annotations?.['opendatahub.io/apiProtocol']) { return undefined; } - return resource.metadata.annotations['opendatahub.io/apiProtocol'] as ServingRuntimeAPIProtocol; + return ( + asEnumMember( + resource.metadata.annotations['opendatahub.io/apiProtocol'], + ServingRuntimeAPIProtocol, + ) ?? undefined + ); }; diff --git a/frontend/src/pages/modelServing/screens/global/utils.ts b/frontend/src/pages/modelServing/screens/global/utils.ts index f4098f7c8c..21c1bdf014 100644 --- a/frontend/src/pages/modelServing/screens/global/utils.ts +++ b/frontend/src/pages/modelServing/screens/global/utils.ts @@ -2,6 +2,7 @@ import { InferenceServiceKind, ProjectKind, SecretKind, PodKind } from '~/k8sTyp import { getDisplayNameFromK8sResource } from '~/concepts/k8s/utils'; import { getProjectDisplayName } from '~/concepts/projects/utils'; import { InferenceServiceModelState, ModelStatus } from '~/pages/modelServing/screens/types'; +import { asEnumMember } from '~/utilities/utils'; export const getInferenceServiceDisplayName = (is: InferenceServiceKind): string => getDisplayNameFromK8sResource(is); @@ -12,8 +13,8 @@ export const getTokenDisplayName = (secret: SecretKind): string => export const getInferenceServiceActiveModelState = ( is: InferenceServiceKind, ): InferenceServiceModelState => - is.status?.modelStatus.states.activeModelState || - is.status?.modelStatus.states.targetModelState || + asEnumMember(is.status?.modelStatus.states.activeModelState, InferenceServiceModelState) || + asEnumMember(is.status?.modelStatus.states.targetModelState, InferenceServiceModelState) || InferenceServiceModelState.UNKNOWN; export const getInferenceServiceStatusMessage = (is: InferenceServiceKind): string => { diff --git a/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx b/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx index 85c1684d85..82a4fd840d 100644 --- a/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/EnsureMetricsAvailable.tsx @@ -28,8 +28,9 @@ const EnsureMetricsAvailable: React.FC = ({ let readyCount = 0; metrics.forEach((metric) => { - if (data[metric].error) { - error = data[metric].error as AxiosError; + const axiosError = data[metric].error; + if (axiosError && axiosError instanceof AxiosError) { + error = axiosError; } if (data[metric].loaded) { readyCount++; diff --git a/frontend/src/pages/modelServing/screens/metrics/bias/BiasChart.tsx b/frontend/src/pages/modelServing/screens/metrics/bias/BiasChart.tsx index 89d7018bfe..fea3dc4562 100644 --- a/frontend/src/pages/modelServing/screens/metrics/bias/BiasChart.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/bias/BiasChart.tsx @@ -4,7 +4,7 @@ import { ModelServingMetricsContext } from '~/pages/modelServing/screens/metrics import { BiasMetricConfig } from '~/concepts/trustyai/types'; import { createChartThresholds } from '~/pages/modelServing/screens/metrics/utils'; import { BIAS_CHART_CONFIGS } from '~/pages/modelServing/screens/metrics/const'; -import { PrometheusQueryRangeResponseDataResult } from '~/types'; +import { isPrometheusQueryRangeResponseDataResultArr } from '~/utilities/utils'; export type BiasChartProps = { biasMetricConfig: BiasMetricConfig; @@ -19,28 +19,33 @@ const BiasChart: React.FC = ({ biasMetricConfig }) => { BIAS_CHART_CONFIGS[metricType]; const metric = React.useMemo(() => { - const metricData = data[modelMetricKey].data as PrometheusQueryRangeResponseDataResult[]; - - const values = metricData.find((x) => x.metric.request === id)?.values || []; - - return { - ...data[modelMetricKey], - data: values, - }; + const metricData = data[modelMetricKey].data; + if (isPrometheusQueryRangeResponseDataResultArr(metricData)) { + const values = metricData.find((x) => x.metric.request === id)?.values || []; + + return { + ...data[modelMetricKey], + data: values, + }; + } + return null; }, [data, id, modelMetricKey]); - return ( - - ); + if (metric !== null) { + return ( + + ); + } + return null; }; export default BiasChart; diff --git a/frontend/src/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationModal/MetricTypeField.tsx b/frontend/src/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationModal/MetricTypeField.tsx index fd7c60a5f1..2a3780f846 100644 --- a/frontend/src/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationModal/MetricTypeField.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/bias/BiasConfigurationPage/BiasConfigurationModal/MetricTypeField.tsx @@ -7,6 +7,7 @@ import { } from '~/pages/modelServing/screens/metrics/const'; import { BiasMetricType } from '~/api'; import { isMetricType } from '~/pages/modelServing/screens/metrics/utils'; +import { asEnumMember } from '~/utilities/utils'; type MetricTypeFieldProps = { fieldId: string; @@ -16,6 +17,7 @@ type MetricTypeFieldProps = { const MetricTypeField: React.FC = ({ fieldId, value, onChange }) => { const [isOpen, setOpen] = React.useState(false); + return ( diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx index 8f9c511223..07952416c6 100644 --- a/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/performance/ModelMeshMetrics.tsx @@ -5,11 +5,13 @@ import { ModelMetricType, ModelServingMetricsContext, } from '~/pages/modelServing/screens/metrics/ModelServingMetricsContext'; -import { ContextResourceData, PrometheusQueryRangeResultValue } from '~/types'; import EnsureMetricsAvailable from '~/pages/modelServing/screens/metrics/EnsureMetricsAvailable'; +import { isPrometheusQueryRangeResultValueNotResponseDataResultWithContext } from '~/utilities/utils'; const ModelMeshMetrics: React.FC = () => { const { data } = React.useContext(ModelServingMetricsContext); + const requestSuccess = data[ModelMetricType.REQUEST_COUNT_SUCCESS]; + const requestCountFailed = data[ModelMetricType.REQUEST_COUNT_FAILED]; return ( @@ -18,25 +20,26 @@ const ModelMeshMetrics: React.FC = () => { accessDomain="model metrics" > - , - }, - { - name: 'Failed', - metric: data[ - ModelMetricType.REQUEST_COUNT_FAILED - ] as ContextResourceData, - }, - ]} - color="blue" - title="HTTP requests per 5 minutes" - isStack - /> + {isPrometheusQueryRangeResultValueNotResponseDataResultWithContext(requestSuccess) && + isPrometheusQueryRangeResultValueNotResponseDataResultWithContext( + requestCountFailed, + ) && ( + + )} diff --git a/frontend/src/pages/modelServing/screens/metrics/performance/ServerGraphs.tsx b/frontend/src/pages/modelServing/screens/metrics/performance/ServerGraphs.tsx index bcf76a0b15..7f0db0783e 100644 --- a/frontend/src/pages/modelServing/screens/metrics/performance/ServerGraphs.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/performance/ServerGraphs.tsx @@ -9,80 +9,82 @@ import { convertPrometheusNaNToZero, toPercentage, } from '~/pages/modelServing/screens/metrics/utils'; -import { - ContextResourceData, - PrometheusQueryRangeResponseDataResult, - PrometheusQueryRangeResultValue, -} from '~/types'; import { NamedMetricChartLine } from '~/pages/modelServing/screens/metrics/types'; +import { + isPrometheusQueryRangeResponseDataResultNotRangeResultValueWithContext, + isPrometheusQueryRangeResultValueNotResponseDataResultWithContext, +} from '~/utilities/utils'; const ServerGraphs: React.FC = () => { const { data } = React.useContext(ModelServingMetricsContext); + const requestCount = data[ServerMetricType.REQUEST_COUNT]; + const avgResponseTime = data[ServerMetricType.AVG_RESPONSE_TIME]; + const cpuUtils = data[ServerMetricType.CPU_UTILIZATION]; + const memoryUtils = data[ServerMetricType.MEMORY_UTILIZATION]; + return ( - - , - }} - color="blue" - title="HTTP requests per 5 minutes" - isStack - /> - - - - ).data.map( - (line): NamedMetricChartLine => ({ - name: line.metric.pod || '', - metric: { - ...data[ServerMetricType.AVG_RESPONSE_TIME], - data: convertPrometheusNaNToZero(line.values), - }, - }), - )} - color="green" - title="Average response time (ms)" - isStack - /> - - - , - translatePoint: toPercentage, - }} - color="purple" - title="CPU utilization %" - domain={() => ({ - y: [0, 100], - })} - /> - - - , - translatePoint: toPercentage, - }} - color="orange" - title="Memory utilization %" - domain={() => ({ - y: [0, 100], - })} - /> - + {isPrometheusQueryRangeResultValueNotResponseDataResultWithContext(requestCount) && ( + + + + )} + {isPrometheusQueryRangeResponseDataResultNotRangeResultValueWithContext(avgResponseTime) && ( + + ({ + name: line.metric.pod || '', + metric: { + ...data[ServerMetricType.AVG_RESPONSE_TIME], + data: convertPrometheusNaNToZero(line.values), + }, + }), + )} + color="green" + title="Average response time (ms)" + isStack + /> + + )}{' '} + {isPrometheusQueryRangeResultValueNotResponseDataResultWithContext(cpuUtils) && ( + + ({ + y: [0, 100], + })} + /> + + )} + {isPrometheusQueryRangeResultValueNotResponseDataResultWithContext(memoryUtils) && ( + + ({ + y: [0, 100], + })} + /> + + )} ); }; diff --git a/frontend/src/pages/modelServing/screens/metrics/utils.tsx b/frontend/src/pages/modelServing/screens/metrics/utils.tsx index f82f06e592..2f878d30cd 100644 --- a/frontend/src/pages/modelServing/screens/metrics/utils.tsx +++ b/frontend/src/pages/modelServing/screens/metrics/utils.tsx @@ -14,6 +14,7 @@ import { BIAS_THRESHOLD_COLOR, } from '~/pages/modelServing/screens/metrics/const'; import { PROMETHEUS_REQUEST_RESOLUTION } from '~/concepts/metrics/const'; +import { isEnumMember } from '~/utilities/utils'; import { BiasSelectOption, DomainCalculator, @@ -219,8 +220,7 @@ export const checkConfigurationFieldsValid = ( export const isMetricType = ( metricType: string | SelectOptionObject, -): metricType is BiasMetricType => - Object.values(BiasMetricType).includes(metricType as BiasMetricType); +): metricType is BiasMetricType => isEnumMember(metricType.toString(), BiasMetricType); export const byId = (arg: U): ((arg: T) => boolean) => diff --git a/frontend/src/pages/notebookController/screens/admin/NotebookControllerTabs.tsx b/frontend/src/pages/notebookController/screens/admin/NotebookControllerTabs.tsx index e85b2193fb..0a76b36591 100644 --- a/frontend/src/pages/notebookController/screens/admin/NotebookControllerTabs.tsx +++ b/frontend/src/pages/notebookController/screens/admin/NotebookControllerTabs.tsx @@ -3,6 +3,7 @@ import { Tab, Tabs, TabTitleText } from '@patternfly/react-core'; import { NotebookControllerTabTypes } from '~/pages/notebookController/const'; import NotebookServerRoutes from '~/pages/notebookController/screens/server/NotebookServerRoutes'; import { NotebookControllerContext } from '~/pages/notebookController/NotebookControllerContext'; +import { asEnumMember } from '~/utilities/utils'; import NotebookAdmin from './NotebookAdmin'; const NotebookControllerTabs: React.FC = () => { @@ -16,7 +17,10 @@ const NotebookControllerTabs: React.FC = () => { unmountOnExit onSelect={(e, eventKey) => { setImpersonating(); - setCurrentAdminTab(eventKey as NotebookControllerTabTypes); + const enumValue = asEnumMember(eventKey, NotebookControllerTabTypes); + if (enumValue !== null) { + setCurrentAdminTab(enumValue); + } }} > = ({ user, userProperty }) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const content = user[userProperty as keyof AdminViewUserData]; if (isField(content, userProperty === 'serverStatus')) { diff --git a/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesField.tsx b/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesField.tsx index 7234cad59f..37fc1ab4e2 100644 --- a/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesField.tsx +++ b/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesField.tsx @@ -17,6 +17,7 @@ import { CUSTOM_VARIABLE, EMPTY_KEY } from '~/pages/notebookController/const'; import { EnvVarType, VariableRow } from '~/types'; import '~/pages/notebookController/NotebookController.scss'; +import { asEnumMember } from '~/utilities/utils'; type EnvironmentVariablesFieldProps = { fieldIndex: string; @@ -88,7 +89,7 @@ const EnvironmentVariablesField: React.FC = ({ type={ showPassword && variableType === 'password' ? TextInputTypes.text - : (variable.type as TextInputTypes) + : asEnumMember(variable.type, TextInputTypes) ?? undefined } value={variable.value} onChange={(e, newValue) => diff --git a/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesRow.tsx b/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesRow.tsx index 84a99a7aca..4b3192712e 100644 --- a/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesRow.tsx +++ b/frontend/src/pages/notebookController/screens/server/EnvironmentVariablesRow.tsx @@ -82,7 +82,9 @@ const EnvironmentVariablesRow: React.FC = ({ onToggle={() => setTypeDropdownOpen(!typeDropdownOpen)} aria-labelledby="container-size" selections={variableRow.variableType} - onSelect={(e, selection) => updateVariableType(selection as string)} + onSelect={(e, selection) => { + updateVariableType(selection.toString()); + }} > {selectOptions} diff --git a/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx b/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx index 1090db9ce6..a1353d144a 100644 --- a/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx +++ b/frontend/src/pages/pipelines/global/experiments/executions/details/ExecutionDetails.tsx @@ -44,10 +44,10 @@ const ExecutionDetails: PipelineCoreDetailsPageComponent = ({ breadcrumbPath, co const [artifactTypes, artifactTypesLoaded] = useGetArtifactTypes(); const allEvents = parseEventsByType(events); - const artifactTypeMap = artifactTypes.reduce((acc, artifactType) => { + const artifactTypeMap = artifactTypes.reduce>((acc, artifactType) => { acc[artifactType.getId()] = artifactType.getName(); return acc; - }, {} as Record); + }, {}); const error = executionError || eventsError; diff --git a/frontend/src/pages/pipelines/global/runs/GlobalPipelineRunsTabs.tsx b/frontend/src/pages/pipelines/global/runs/GlobalPipelineRunsTabs.tsx index a2e2e6a45e..62ddbacdfd 100644 --- a/frontend/src/pages/pipelines/global/runs/GlobalPipelineRunsTabs.tsx +++ b/frontend/src/pages/pipelines/global/runs/GlobalPipelineRunsTabs.tsx @@ -30,7 +30,12 @@ const GlobalPipelineRunsTab: React.FC = () => { return ( setSearchParams({ runType: tabId as PipelineRunType })} + onSelect={(_event, tabId) => { + const enumValue = asEnumMember(tabId, PipelineRunType); + if (enumValue !== null) { + setSearchParams({ runType: enumValue }); + } + }} aria-label="Pipeline run page tabs" role="region" className="odh-pipeline-runs-page-tabs" diff --git a/frontend/src/pages/projects/ProjectDetailsContext.tsx b/frontend/src/pages/projects/ProjectDetailsContext.tsx index f7e64820a5..b9873e7b8d 100644 --- a/frontend/src/pages/projects/ProjectDetailsContext.tsx +++ b/frontend/src/pages/projects/ProjectDetailsContext.tsx @@ -54,6 +54,7 @@ type ProjectDetailsContextType = { export const ProjectDetailsContext = React.createContext({ // We never will get into a case without a project, so fudge the default value + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions currentProject: null as unknown as ProjectKind, refreshAllProjectData: () => undefined, filterTokens: () => [], diff --git a/frontend/src/pages/projects/screens/detail/overview/serverModels/deployedModels/DeployedModelsSection.tsx b/frontend/src/pages/projects/screens/detail/overview/serverModels/deployedModels/DeployedModelsSection.tsx index 6a2b420559..0561d4d81e 100644 --- a/frontend/src/pages/projects/screens/detail/overview/serverModels/deployedModels/DeployedModelsSection.tsx +++ b/frontend/src/pages/projects/screens/detail/overview/serverModels/deployedModels/DeployedModelsSection.tsx @@ -58,11 +58,14 @@ const DeployedModelsSection: React.FC = ({ isMultiPl return; } if (isMultiPlatform) { - const modelInferenceServices = modelServers.reduce((acc, modelServer) => { - const services = getInferenceServiceFromServingRuntime(inferenceServices, modelServer); - acc.push(...services); - return acc; - }, [] as InferenceServiceKind[]); + const modelInferenceServices = modelServers.reduce( + (acc, modelServer) => { + const services = getInferenceServiceFromServingRuntime(inferenceServices, modelServer); + acc.push(...services); + return acc; + }, + [], + ); setDeployedModels(modelInferenceServices); } else { setDeployedModels(inferenceServices); diff --git a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvConfigMap.tsx b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvConfigMap.tsx index 087af37f9d..7d876cb12b 100644 --- a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvConfigMap.tsx +++ b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvConfigMap.tsx @@ -4,6 +4,7 @@ import { EnvironmentVariableType, EnvVariableData, } from '~/pages/projects/types'; +import { asEnumMember } from '~/utilities/utils'; import EnvDataTypeField from './EnvDataTypeField'; import GenericKeyValuePairField from './GenericKeyValuePairField'; import { EMPTY_KEY_VALUE_PAIR } from './const'; @@ -22,7 +23,9 @@ const DEFAULT_ENV: EnvVariableData = { const EnvConfigMap: React.FC = ({ env = DEFAULT_ENV, onUpdate }) => ( onUpdate({ ...env, category: value as ConfigMapCategory, data: [] })} + onSelection={(value) => + onUpdate({ ...env, category: asEnumMember(value, ConfigMapCategory), data: [] }) + } options={{ [ConfigMapCategory.GENERIC]: { label: 'Key / value', diff --git a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvSecret.tsx b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvSecret.tsx index b5abf71d37..aab8fe07c0 100644 --- a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvSecret.tsx +++ b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvSecret.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import { EnvironmentVariableType, EnvVariableData, SecretCategory } from '~/pages/projects/types'; +import { asEnumMember } from '~/utilities/utils'; import EnvDataTypeField from './EnvDataTypeField'; import GenericKeyValuePairField from './GenericKeyValuePairField'; import { EMPTY_KEY_VALUE_PAIR } from './const'; @@ -18,7 +19,9 @@ const DEFAULT_ENV: EnvVariableData = { const EnvSecret: React.FC = ({ env = DEFAULT_ENV, onUpdate }) => ( onUpdate({ ...env, category: value as SecretCategory, data: [] })} + onSelection={(value) => + onUpdate({ ...env, category: asEnumMember(value, SecretCategory), data: [] }) + } options={{ [SecretCategory.GENERIC]: { label: 'Key / value', diff --git a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvTypeSelectField.tsx b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvTypeSelectField.tsx index 259f796cb3..e3ecc96984 100644 --- a/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvTypeSelectField.tsx +++ b/frontend/src/pages/projects/screens/spawner/environmentVariables/EnvTypeSelectField.tsx @@ -4,6 +4,7 @@ import { Select, SelectOption } from '@patternfly/react-core/deprecated'; import { MinusCircleIcon } from '@patternfly/react-icons'; import { EnvironmentVariableType, EnvVariable } from '~/pages/projects/types'; import IndentSection from '~/pages/projects/components/IndentSection'; +import { asEnumMember } from '~/utilities/utils'; import EnvTypeSwitch from './EnvTypeSwitch'; type EnvTypeSelectFieldProps = { @@ -32,10 +33,13 @@ const EnvTypeSelectField: React.FC = ({ aria-label="Select environment variable type" onSelect={(e, value) => { if (typeof value === 'string') { - onUpdate({ - type: value as EnvironmentVariableType, - }); - setOpen(false); + const enumValue = asEnumMember(value, EnvironmentVariableType); + if (enumValue !== null) { + onUpdate({ + type: asEnumMember(value, EnvironmentVariableType), + }); + setOpen(false); + } } }} > diff --git a/frontend/src/pages/projects/screens/spawner/environmentVariables/utils.ts b/frontend/src/pages/projects/screens/spawner/environmentVariables/utils.ts index d49b49aae5..e73f077feb 100644 --- a/frontend/src/pages/projects/screens/spawner/environmentVariables/utils.ts +++ b/frontend/src/pages/projects/screens/spawner/environmentVariables/utils.ts @@ -7,12 +7,14 @@ export const removeArrayItem = (values: T[], index: number): T[] => values.filter((v, i) => i !== index); export const isConfigMapKind = (object: unknown): object is ConfigMapKind => - (object as ConfigMapKind).kind === 'ConfigMap'; + typeof object === 'object' && object !== null && 'kind' in object && object.kind === 'ConfigMap'; export const isSecretKind = (object: unknown): object is SecretKind => - (object as SecretKind).kind === 'Secret'; + typeof object === 'object' && object !== null && 'kind' in object && object.kind === 'Secret'; export const isStringKeyValuePairObject = (object: unknown): object is Record => - Object.entries(object as Record).every( + typeof object === 'object' && + object !== null && + Object.entries(object).every( ([key, value]) => typeof key === 'string' && typeof value === 'string', ); diff --git a/frontend/src/pages/projects/screens/spawner/spawnerUtils.ts b/frontend/src/pages/projects/screens/spawner/spawnerUtils.ts index 54bbf8397f..b68507988d 100644 --- a/frontend/src/pages/projects/screens/spawner/spawnerUtils.ts +++ b/frontend/src/pages/projects/screens/spawner/spawnerUtils.ts @@ -81,7 +81,10 @@ export const getImageVersionSelectOptionObject = ( export const isImageVersionSelectOptionObject = ( object: unknown, ): object is ImageVersionSelectOptionObjectType => - (object as ImageVersionSelectOptionObjectType | undefined)?.imageVersion !== undefined; + typeof object === 'object' && + object !== null && + 'imageVersion' in object && + object.imageVersion !== undefined; /******************* Compare utils for sorting *******************/ const getBuildNumber = (build: BuildKind): number => { const buildNumber = build.metadata.annotations?.['openshift.io/build.number'] || '-1'; diff --git a/frontend/src/pages/projects/screens/spawner/useBuildStatuses.ts b/frontend/src/pages/projects/screens/spawner/useBuildStatuses.ts index 4c2c3987f3..0e08b4e05f 100644 --- a/frontend/src/pages/projects/screens/spawner/useBuildStatuses.ts +++ b/frontend/src/pages/projects/screens/spawner/useBuildStatuses.ts @@ -1,7 +1,7 @@ import * as React from 'react'; import { getNotebookBuildConfigs, getBuildsForBuildConfig } from '~/api'; import useNotification from '~/utilities/useNotification'; -import { BuildConfigKind, BuildKind, BuildPhase } from '~/k8sTypes'; +import { BuildConfigKind, BuildPhase } from '~/k8sTypes'; import { BuildStatus } from './types'; import { compareBuilds } from './spawnerUtils'; @@ -26,7 +26,7 @@ const useBuildStatuses = (namespace?: string): BuildStatus[] => { imageStreamVersion: buildConfig.spec.output.to.name, }; } - const mostRecent = builds.toSorted(compareBuilds).pop() as BuildKind; + const mostRecent = builds.toSorted(compareBuilds)[builds.length - 1]; return { name: buildNotebookName, status: mostRecent.status.phase, diff --git a/frontend/src/redux/context.ts b/frontend/src/redux/context.ts index 82105b8f35..e93bf2751a 100644 --- a/frontend/src/redux/context.ts +++ b/frontend/src/redux/context.ts @@ -2,5 +2,6 @@ import * as React from 'react'; import { ReactReduxContextValue } from 'react-redux'; export const ReduxContext = React.createContext( + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions {} as ReactReduxContextValue, ); diff --git a/frontend/src/redux/store/store.ts b/frontend/src/redux/store/store.ts index 5eb7522efd..aa01b385ce 100644 --- a/frontend/src/redux/store/store.ts +++ b/frontend/src/redux/store/store.ts @@ -5,7 +5,7 @@ import appReducer from '~/redux/reducers/appReducer'; import { ODH_PRODUCT_NAME } from '~/utilities/const'; const composeEnhancers = - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?.({ name: ODH_PRODUCT_NAME, }) || compose; @@ -16,7 +16,7 @@ export const store = configureStore(); // Create a separate for the for the dynamic plugin SDK const sdkComposeEnhancers = - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/consistent-type-assertions (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__?.({ name: `${ODH_PRODUCT_NAME} - Dynamic Plugin SDK`, }) || compose; diff --git a/frontend/src/typeHelpers.ts b/frontend/src/typeHelpers.ts index 6df5120c04..e563587054 100644 --- a/frontend/src/typeHelpers.ts +++ b/frontend/src/typeHelpers.ts @@ -162,6 +162,7 @@ export type ExactlyOne = AtMostOne & AtLeastOne; export const isInEnum = (e: T) => (token: unknown): token is T[keyof T] => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions Object.values(e).includes(token as T[keyof T]); /** diff --git a/frontend/src/utilities/NavData.tsx b/frontend/src/utilities/NavData.tsx index f8926a7072..285f9d2ca3 100644 --- a/frontend/src/utilities/NavData.tsx +++ b/frontend/src/utilities/NavData.tsx @@ -29,8 +29,10 @@ export type NavDataGroup = NavDataCommon & { export type NavDataItem = NavDataHref | NavDataGroup; export const isNavDataHref = (navData: NavDataItem): navData is NavDataHref => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions !!(navData as NavDataHref).href; export const isNavDataGroup = (navData: NavDataItem): navData is NavDataGroup => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions !!(navData as NavDataGroup).children; const useAreaCheck = (area: SupportedArea, success: T[]): T[] => diff --git a/frontend/src/utilities/useAcceleratorProfileState.ts b/frontend/src/utilities/useAcceleratorProfileState.ts index f777fc594b..def476a904 100644 --- a/frontend/src/utilities/useAcceleratorProfileState.ts +++ b/frontend/src/utilities/useAcceleratorProfileState.ts @@ -10,7 +10,7 @@ import { TolerationOperator, } from '~/types'; import useGenericObjectState, { GenericObjectState } from '~/utilities/useGenericObjectState'; -import { getAcceleratorProfileCount } from '~/utilities/utils'; +import { getAcceleratorProfileCount, isEnumMember } from '~/utilities/utils'; export type AcceleratorProfileState = { acceleratorProfile?: AcceleratorProfileKind; @@ -65,9 +65,9 @@ const useAcceleratorProfileState = ( } else { // check if there is accelerator usage in the container // this is to handle the case where the accelerator is disabled, deleted, or empty - const containerResourceAttributes = Object.values(ContainerResourceAttributes) as string[]; + const containerResourceAttributes = Object.values(ContainerResourceAttributes); const possibleAcceleratorRequests = Object.entries(resources.requests ?? {}) - .filter(([key]) => !containerResourceAttributes.includes(key)) + .filter(([key]) => isEnumMember(key, containerResourceAttributes)) .map(([key, value]) => ({ identifier: key, count: value })); if (possibleAcceleratorRequests.length > 0) { // check if they are just using the nvidia.com/gpu diff --git a/frontend/src/utilities/useDraggableTable.ts b/frontend/src/utilities/useDraggableTable.ts index 6fa8540dcb..b812d4be01 100644 --- a/frontend/src/utilities/useDraggableTable.ts +++ b/frontend/src/utilities/useDraggableTable.ts @@ -125,7 +125,7 @@ const useDraggableTable = ( return; } - const curListItem = (evt.target as HTMLTableSectionElement).closest('tr'); + const curListItem = evt.target instanceof HTMLElement ? evt.target.closest('tr') : null; if ( !curListItem || !bodyRef.current.contains(curListItem) || @@ -159,6 +159,7 @@ const useDraggableTable = ( ); const onDragEnd = React.useCallback((evt) => { + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions const target = evt.currentTarget as HTMLTableRowElement; target.classList.remove(styles.modifiers.ghostRow); target.setAttribute('aria-pressed', 'false'); diff --git a/frontend/src/utilities/useWatchNotebooksForUsers.tsx b/frontend/src/utilities/useWatchNotebooksForUsers.tsx index 13a09e4f75..9451a16d0c 100644 --- a/frontend/src/utilities/useWatchNotebooksForUsers.tsx +++ b/frontend/src/utilities/useWatchNotebooksForUsers.tsx @@ -54,10 +54,13 @@ const useWatchNotebooksForUsers = ( setLoadError(undefined); } - const newNotebooks = successes.reduce((acc, { value }) => { - acc[value.name] = value.data; - return acc; - }, {} as UsernameMap); + const newNotebooks = successes.reduce>( + (acc, { value }) => { + acc[value.name] = value.data; + return acc; + }, + {}, + ); setNotebooks((prevState) => ({ ...prevState, ...newNotebooks })); setLoaded(true); }) diff --git a/frontend/src/utilities/utils.ts b/frontend/src/utilities/utils.ts index 2b7def86e2..7443b88a20 100644 --- a/frontend/src/utilities/utils.ts +++ b/frontend/src/utilities/utils.ts @@ -1,5 +1,13 @@ import { LabelProps } from '@patternfly/react-core'; -import { ContainerResources, OdhApplication, OdhDocument, OdhDocumentType } from '~/types'; +import { + ContainerResources, + ContextResourceData, + OdhApplication, + OdhDocument, + OdhDocumentType, + PrometheusQueryRangeResponseDataResult, + PrometheusQueryRangeResultValue, +} from '~/types'; import { AcceleratorProfileKind } from '~/k8sTypes'; import { CATEGORY_ANNOTATION, DASHBOARD_MAIN_CONTAINER_ID, ODH_PRODUCT_NAME } from './const'; @@ -140,8 +148,43 @@ export const getDashboardMainContainer = (): HTMLElement => document.getElementById(DASHBOARD_MAIN_CONTAINER_ID) || document.body; export const isHTMLInputElement = (object: unknown): object is HTMLInputElement => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions (object as Partial).value !== undefined; +export const isHTMLInputElementChecked = ( + object: unknown, +): object is React.AllHTMLAttributes => + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + (object as React.AllHTMLAttributes).checked !== undefined; + +export const isPrometheusQueryRangeResultValueNotResponseDataResultWithContext = ( + obj: unknown, +): obj is ContextResourceData => + typeof obj === 'object' && + obj !== null && + 'data' in obj && + isPrometheusQueryRangeResultValueArr(obj.data); + +export const isPrometheusQueryRangeResponseDataResultNotRangeResultValueWithContext = ( + obj: unknown, +): obj is ContextResourceData => + typeof obj === 'object' && + obj !== null && + 'data' in obj && + isPrometheusQueryRangeResponseDataResultArr(obj.data); + +export const isPrometheusQueryRangeResultValueArr = ( + obj: unknown, +): obj is PrometheusQueryRangeResultValue[] => + Array.isArray(obj) && + obj.every((ele) => typeof ele[0] === 'number' && typeof ele[1] === 'string'); + +export const isPrometheusQueryRangeResponseDataResultArr = ( + obj: unknown, +): obj is PrometheusQueryRangeResponseDataResult[] => + Array.isArray(obj) && + obj.every((ele) => typeof ele === 'object' && 'metric' in ele && 'values' in ele); + export const normalizeBetween = (value: number, min?: number, max?: number): number => { let returnedValue = value; if (min !== undefined && max !== undefined) { @@ -160,12 +203,12 @@ export const getAcceleratorProfileCount = ( ): number => Number(resources.requests?.[acceleratorProfile.spec.identifier] ?? 0); export const asEnumMember = ( - member: T[keyof T] | string | number | null, + member: T[keyof T] | string | number | undefined | null, e: T, ): T[keyof T] | null => (isEnumMember(member, e) ? member : null); export const isEnumMember = ( - member: T[keyof T] | string | number | null, + member: T[keyof T] | string | number | undefined | null, e: T, ): member is T[keyof T] => { if (member != null) {