From 53be1414605a1d218906dcee75392e6e6bc9dc19 Mon Sep 17 00:00:00 2001 From: Kenrick Tandrian <60643640+KenTandrian@users.noreply.github.com> Date: Mon, 26 Jan 2026 01:00:26 +0700 Subject: [PATCH] Feature/enable strict null checks in libs/common (#6250) * feat(ts): enable strict null checks in libs/common * feat(lint): enable prefer-nullish-coalescing * feat(lib): resolve errors * fix(lib): revert changes on DateRange type --- libs/common/eslint.config.cjs | 4 ++- libs/common/src/lib/chart-helper.ts | 26 ++++++++++++------- libs/common/src/lib/class-transformer.ts | 2 +- libs/common/src/lib/helper.ts | 8 +++--- .../public-portfolio-response.interface.ts | 2 +- .../interfaces/internal-route.interface.ts | 2 +- libs/common/src/lib/utils/form.util.ts | 4 +-- .../src/lib/validators/is-currency-code.ts | 2 +- libs/common/tsconfig.json | 3 ++- 9 files changed, 31 insertions(+), 22 deletions(-) diff --git a/libs/common/eslint.config.cjs b/libs/common/eslint.config.cjs index a78dde897..990c264b4 100644 --- a/libs/common/eslint.config.cjs +++ b/libs/common/eslint.config.cjs @@ -18,7 +18,9 @@ module.exports = [ { files: ['**/*.ts', '**/*.tsx'], // Override or add rules here - rules: {} + rules: { + '@typescript-eslint/prefer-nullish-coalescing': 'error' + } }, { files: ['**/*.js', '**/*.jsx'], diff --git a/libs/common/src/lib/chart-helper.ts b/libs/common/src/lib/chart-helper.ts index 697f39467..da6473645 100644 --- a/libs/common/src/lib/chart-helper.ts +++ b/libs/common/src/lib/chart-helper.ts @@ -1,4 +1,10 @@ -import { Chart, TooltipPosition } from 'chart.js'; +import type { ElementRef } from '@angular/core'; +import type { + Chart, + ChartTypeRegistry, + Plugin, + TooltipPosition +} from 'chart.js'; import { format } from 'date-fns'; import { @@ -34,12 +40,12 @@ export function getTooltipOptions({ locale = getLocale(), unit = '' }: { - colorScheme?: ColorScheme; + colorScheme: ColorScheme; currency?: string; groupBy?: GroupBy; locale?: string; unit?: string; -} = {}) { +}) { return { backgroundColor: getBackgroundColor(colorScheme), bodyColor: `rgb(${getTextColor(colorScheme)})`, @@ -47,7 +53,7 @@ export function getTooltipOptions({ borderColor: `rgba(${getTextColor(colorScheme)}, 0.1)`, callbacks: { label: (context) => { - let label = context.dataset.label || ''; + let label = context.dataset.label ?? ''; if (label) { label += ': '; } @@ -98,10 +104,10 @@ export function getTooltipPositionerMapTop( }; } -export function getVerticalHoverLinePlugin( - chartCanvas, - colorScheme?: ColorScheme -) { +export function getVerticalHoverLinePlugin( + chartCanvas: ElementRef, + colorScheme: ColorScheme +): Plugin { return { afterDatasetsDraw: (chart, _, options) => { const active = chart.getActiveElements(); @@ -110,8 +116,8 @@ export function getVerticalHoverLinePlugin( return; } - const color = options.color || `rgb(${getTextColor(colorScheme)})`; - const width = options.width || 1; + const color = options.color ?? `rgb(${getTextColor(colorScheme)})`; + const width = options.width ?? 1; const { chartArea: { bottom, top } diff --git a/libs/common/src/lib/class-transformer.ts b/libs/common/src/lib/class-transformer.ts index 328e2bf9e..60e0eab60 100644 --- a/libs/common/src/lib/class-transformer.ts +++ b/libs/common/src/lib/class-transformer.ts @@ -16,7 +16,7 @@ export function transformToMapOfBig({ return mapOfBig; } -export function transformToBig({ value }: { value: string }): Big { +export function transformToBig({ value }: { value: string }): Big | null { if (value === null) { return null; } diff --git a/libs/common/src/lib/helper.ts b/libs/common/src/lib/helper.ts index cb4c0e1b7..9ee7d6220 100644 --- a/libs/common/src/lib/helper.ts +++ b/libs/common/src/lib/helper.ts @@ -144,7 +144,7 @@ export function extractNumberFromString({ }: { locale?: string; value: string; -}): number { +}): number | undefined { try { // Remove non-numeric characters (excluding international formatting characters) const numericValue = value.replace(/[^\d.,'’\s]/g, ''); @@ -273,7 +273,7 @@ export function getNumberFormatDecimal(aLocale?: string) { return formatObject.find((object) => { return object.type === 'decimal'; - }).value; + })?.value; } export function getNumberFormatGroup(aLocale = getLocale()) { @@ -283,7 +283,7 @@ export function getNumberFormatGroup(aLocale = getLocale()) { return formatObject.find((object) => { return object.type === 'group'; - }).value; + })?.value; } export function getStartOfUtcDate(aDate: Date) { @@ -394,7 +394,7 @@ export function isRootCurrency(aCurrency: string) { }); } -export function parseDate(date: string): Date { +export function parseDate(date: string): Date | undefined { if (!date) { return undefined; } diff --git a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts index cb06800be..4a087ad16 100644 --- a/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts +++ b/libs/common/src/lib/interfaces/responses/public-portfolio-response.interface.ts @@ -39,7 +39,7 @@ export interface PublicPortfolioResponse extends PublicPortfolioResponseV1 { })[]; markets: { [key in Market]: Pick< - PortfolioDetails['markets'][key], + NonNullable[key], 'id' | 'valueInPercentage' >; }; diff --git a/libs/common/src/lib/routes/interfaces/internal-route.interface.ts b/libs/common/src/lib/routes/interfaces/internal-route.interface.ts index 02f205979..f08cf8b5c 100644 --- a/libs/common/src/lib/routes/interfaces/internal-route.interface.ts +++ b/libs/common/src/lib/routes/interfaces/internal-route.interface.ts @@ -2,7 +2,7 @@ import { User } from '@ghostfolio/common/interfaces'; export interface InternalRoute { excludeFromAssistant?: boolean | ((aUser: User) => boolean); - path: string; + path?: string; routerLink: string[]; subRoutes?: Record; title: string; diff --git a/libs/common/src/lib/utils/form.util.ts b/libs/common/src/lib/utils/form.util.ts index 425aa4699..b510e6215 100644 --- a/libs/common/src/lib/utils/form.util.ts +++ b/libs/common/src/lib/utils/form.util.ts @@ -29,7 +29,7 @@ export async function validateObjectForForm({ if (formControl) { formControl.setErrors({ - validationError: Object.values(constraints)[0] + validationError: Object.values(constraints ?? {})[0] }); } @@ -37,7 +37,7 @@ export async function validateObjectForForm({ if (formControlInCustomCurrency) { formControlInCustomCurrency.setErrors({ - validationError: Object.values(constraints)[0] + validationError: Object.values(constraints ?? {})[0] }); } } diff --git a/libs/common/src/lib/validators/is-currency-code.ts b/libs/common/src/lib/validators/is-currency-code.ts index 76c6f4fe2..52d99816b 100644 --- a/libs/common/src/lib/validators/is-currency-code.ts +++ b/libs/common/src/lib/validators/is-currency-code.ts @@ -9,7 +9,7 @@ import { import { isISO4217CurrencyCode } from 'class-validator'; export function IsCurrencyCode(validationOptions?: ValidationOptions) { - return function (object: Object, propertyName: string) { + return function (object: object, propertyName: string) { registerDecorator({ propertyName, constraints: [], diff --git a/libs/common/tsconfig.json b/libs/common/tsconfig.json index a14e0fc44..2b4603b71 100644 --- a/libs/common/tsconfig.json +++ b/libs/common/tsconfig.json @@ -12,6 +12,7 @@ ], "compilerOptions": { "module": "preserve", - "lib": ["dom", "es2022"] + "lib": ["dom", "es2022"], + "strictNullChecks": true } }