From 8905893dbb41d9b29d8cf3535bac5e23e9027d33 Mon Sep 17 00:00:00 2001 From: KenTandrian Date: Wed, 4 Feb 2026 23:51:58 +0700 Subject: [PATCH] fix(ui): portfolio proportion chart type safety --- .../portfolio-proportion-chart.component.ts | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts index fb11897eb..33ab63dd3 100644 --- a/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts +++ b/libs/ui/src/lib/portfolio-proportion-chart/portfolio-proportion-chart.component.ts @@ -22,11 +22,16 @@ import { } from '@angular/core'; import { DataSource } from '@prisma/client'; import { Big } from 'big.js'; -import { ChartConfiguration, Tooltip } from 'chart.js'; -import { LinearScale } from 'chart.js'; -import { ArcElement } from 'chart.js'; -import { DoughnutController } from 'chart.js'; -import { Chart } from 'chart.js'; +import { + ArcElement, + Chart, + type ChartData, + type ChartDataset, + DoughnutController, + LinearScale, + Tooltip, + type TooltipOptions +} from 'chart.js'; import ChartDataLabels from 'chartjs-plugin-datalabels'; import { isUUID } from 'class-validator'; import Color from 'color'; @@ -286,7 +291,7 @@ export class GfPortfolioProportionChartComponent }); }); - const datasets: ChartConfiguration<'doughnut'>['data']['datasets'] = [ + const datasets: ChartDataset<'doughnut'>[] = [ { backgroundColor: chartDataSorted.map(([, item]) => { return item.color; @@ -324,7 +329,7 @@ export class GfPortfolioProportionChartComponent datasets[1].data[1] = Number.MAX_SAFE_INTEGER; } - const data: ChartConfiguration<'doughnut'>['data'] = { + const data: ChartData<'doughnut'> = { datasets, labels }; @@ -332,9 +337,9 @@ export class GfPortfolioProportionChartComponent if (this.chartCanvas) { if (this.chart) { this.chart.data = data; - this.chart.options.plugins.tooltip = this.getTooltipPluginConfiguration( - data - ) as unknown; + this.chart.options.plugins ??= {}; + this.chart.options.plugins.tooltip = + this.getTooltipPluginConfiguration(data); this.chart.update(); } else { this.chart = new Chart<'doughnut'>(this.chartCanvas.nativeElement, { @@ -345,21 +350,22 @@ export class GfPortfolioProportionChartComponent layout: { padding: this.showLabels === true ? 100 : 0 }, - onClick: (event, activeElements) => { + onClick: (_, activeElements, chart) => { try { const dataIndex = activeElements[0].index; - const symbol: string = event.chart.data.labels[dataIndex]; + const symbol = chart.data.labels?.[dataIndex] as string; - const dataSource = this.data[symbol]?.dataSource; + const dataSource = this.data[symbol].dataSource; - this.proportionChartClicked.emit({ dataSource, symbol }); + if (dataSource) { + this.proportionChartClicked.emit({ dataSource, symbol }); + } } catch {} }, onHover: (event, chartElement) => { if (this.cursor) { - event.native.target.style.cursor = chartElement[0] - ? this.cursor - : 'default'; + (event.native?.target as HTMLElement).style.cursor = + chartElement[0] ? this.cursor : 'default'; } }, plugins: { @@ -392,7 +398,7 @@ export class GfPortfolioProportionChartComponent legend: { display: false }, tooltip: this.getTooltipPluginConfiguration(data) } - } as unknown, + }, plugins: [ChartDataLabels], type: 'doughnut' }); @@ -419,19 +425,23 @@ export class GfPortfolioProportionChartComponent ]; } - private getTooltipPluginConfiguration(data: ChartConfiguration['data']) { + private getTooltipPluginConfiguration( + data: ChartData<'doughnut'> + ): Partial> { return { ...getTooltipOptions({ colorScheme: this.colorScheme, currency: this.baseCurrency, locale: this.locale }), + // @ts-expect-error: no need to set all attributes in callbacks. callbacks: { label: (context) => { const labelIndex = (data.datasets[context.datasetIndex - 1]?.data?.length ?? 0) + context.dataIndex; - let symbol = context.chart.data.labels?.[labelIndex] ?? ''; + let symbol = + (context.chart.data.labels?.[labelIndex] as string) ?? ''; if (symbol === this.OTHER_KEY) { symbol = $localize`Other`; @@ -439,7 +449,7 @@ export class GfPortfolioProportionChartComponent symbol = $localize`No data available`; } - const name = translate(this.data[symbol as string]?.name); + const name = translate(this.data[symbol]?.name); let sum = 0; for (const item of context.dataset.data) {