mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 02:36:05 +00:00
refactor: improve typing
This commit is contained in:
parent
5bfa9539f1
commit
632c140894
39 changed files with 181 additions and 93 deletions
|
|
@ -15,7 +15,7 @@ import { debounce } from '../../util/decorators';
|
|||
providedIn: 'root',
|
||||
})
|
||||
export class SnackService {
|
||||
private _store$ = inject<Store<any>>(Store);
|
||||
private _store$ = inject(Store);
|
||||
private _translateService = inject(TranslateService);
|
||||
private _actions$ = inject(Actions);
|
||||
private _matSnackBar = inject(MatSnackBar);
|
||||
|
|
@ -80,7 +80,7 @@ export class SnackService {
|
|||
|
||||
if (showWhile$ || promise || isSpinner) {
|
||||
// TODO check if still needed
|
||||
(cfg as any).panelClass = 'polling-snack';
|
||||
(cfg as { panelClass: string }).panelClass = 'polling-snack';
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ import { clockStringFromDate } from '../../../ui/duration/clock-string-from-date
|
|||
import { HelpSectionComponent } from '../../../ui/help-section/help-section.component';
|
||||
import { ChipListInputComponent } from '../../../ui/chip-list-input/chip-list-input.component';
|
||||
import { MatButton } from '@angular/material/button';
|
||||
import { AsyncPipe } from '@angular/common';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { Log } from '../../../core/log';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
|
|
@ -59,7 +58,6 @@ import { toSignal } from '@angular/core/rxjs-interop';
|
|||
ChipListInputComponent,
|
||||
MatDialogActions,
|
||||
MatButton,
|
||||
AsyncPipe,
|
||||
MatIcon,
|
||||
],
|
||||
})
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ import { WorkContextService } from '../work-context/work-context.service';
|
|||
import { ProjectService } from '../project/project.service';
|
||||
import { TaskViewCustomizerService } from '../task-view-customizer/task-view-customizer.service';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { RightPanelComponent } from '../right-panel/right-panel.component';
|
||||
import { CdkDropListGroup } from '@angular/cdk/drag-drop';
|
||||
import { CdkScrollable } from '@angular/cdk/scrolling';
|
||||
import { MatTooltip } from '@angular/material/tooltip';
|
||||
|
|
@ -77,7 +76,6 @@ import { FinishDayBtnComponent } from './finish-day-btn/finish-day-btn.component
|
|||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [
|
||||
RightPanelComponent,
|
||||
CdkDropListGroup,
|
||||
CdkScrollable,
|
||||
MatTooltip,
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ import { MsToClockStringPipe } from '../../ui/duration/ms-to-clock-string.pipe';
|
|||
import { TranslatePipe } from '@ngx-translate/core';
|
||||
import { TaskSummaryTablesComponent } from '../../features/tasks/task-summary-tables/task-summary-tables.component';
|
||||
import { TasksByTagComponent } from '../../features/tasks/tasks-by-tag/tasks-by-tag.component';
|
||||
import { RightPanelComponent } from '../../features/right-panel/right-panel.component';
|
||||
import { EvaluationSheetComponent } from '../../features/metric/evaluation-sheet/evaluation-sheet.component';
|
||||
import { WorklogWeekComponent } from '../../features/worklog/worklog-week/worklog-week.component';
|
||||
import { InlineMarkdownComponent } from '../../ui/inline-markdown/inline-markdown.component';
|
||||
|
|
@ -99,7 +98,6 @@ const MAGIC_YESTERDAY_MARGIN = 4 * 60 * 60 * 1000;
|
|||
TranslatePipe,
|
||||
TaskSummaryTablesComponent,
|
||||
TasksByTagComponent,
|
||||
RightPanelComponent,
|
||||
EvaluationSheetComponent,
|
||||
WorklogWeekComponent,
|
||||
InlineMarkdownComponent,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ interface Suggestion {
|
|||
id: string;
|
||||
title: string;
|
||||
|
||||
[key: string]: any;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
@Component({
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ export class DialogConfirmComponent {
|
|||
|
||||
readonly T: typeof T = T;
|
||||
|
||||
close(res: any): void {
|
||||
close(res: boolean | string | undefined): void {
|
||||
this._matDialogRef.close(res);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,17 @@ import { SimpleDuration } from '../../util/round-duration';
|
|||
|
||||
@Pipe({ name: 'durationFromString' })
|
||||
export class DurationFromStringPipe implements PipeTransform {
|
||||
transform: (value: any, ...args: any[]) => any = durationFromString;
|
||||
transform: (
|
||||
value: string | null | undefined,
|
||||
...args: unknown[]
|
||||
) => SimpleDuration | null = durationFromString;
|
||||
}
|
||||
|
||||
export const durationFromString = (strValue: any, args?: any): SimpleDuration | null => {
|
||||
const milliseconds = stringToMs(strValue);
|
||||
export const durationFromString = (
|
||||
strValue: string | null | undefined,
|
||||
args?: unknown,
|
||||
): SimpleDuration | null => {
|
||||
const milliseconds = stringToMs(strValue || '');
|
||||
if (milliseconds > 0) {
|
||||
return {
|
||||
asMilliseconds: () => milliseconds,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,29 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
type DurationInput =
|
||||
| number
|
||||
| string
|
||||
| { asMilliseconds(): number }
|
||||
| { _milliseconds: number }
|
||||
| {
|
||||
_data: {
|
||||
milliseconds?: number;
|
||||
seconds?: number;
|
||||
minutes?: number;
|
||||
hours?: number;
|
||||
days?: number;
|
||||
};
|
||||
}
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
@Pipe({ name: 'durationToString' })
|
||||
export class DurationToStringPipe implements PipeTransform {
|
||||
transform: (value: any, ...args: any[]) => any = durationToString;
|
||||
transform: (value: DurationInput, ...args: unknown[]) => string = durationToString;
|
||||
}
|
||||
|
||||
/* eslint-disable no-mixed-operators */
|
||||
export const durationToString = (value: any, args?: any): any => {
|
||||
export const durationToString = (value: DurationInput, args?: unknown): string => {
|
||||
if (!value) {
|
||||
return '';
|
||||
}
|
||||
|
|
@ -18,16 +35,31 @@ export const durationToString = (value: any, args?: any): any => {
|
|||
milliseconds = value;
|
||||
}
|
||||
// Handle SimpleDuration object with asMilliseconds method
|
||||
else if (value.asMilliseconds && typeof value.asMilliseconds === 'function') {
|
||||
else if (
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
'asMilliseconds' in value &&
|
||||
typeof value.asMilliseconds === 'function'
|
||||
) {
|
||||
milliseconds = value.asMilliseconds();
|
||||
}
|
||||
// Handle object with _milliseconds property (moment-like)
|
||||
else if (value._milliseconds) {
|
||||
milliseconds = value._milliseconds;
|
||||
else if (typeof value === 'object' && value !== null && '_milliseconds' in value) {
|
||||
milliseconds = (value as { _milliseconds: number })._milliseconds;
|
||||
}
|
||||
// Handle object with _data property (moment duration internal structure)
|
||||
else if (value._data) {
|
||||
const dd = value._data;
|
||||
else if (typeof value === 'object' && value !== null && '_data' in value) {
|
||||
const dd = (
|
||||
value as {
|
||||
_data: {
|
||||
milliseconds?: number;
|
||||
seconds?: number;
|
||||
minutes?: number;
|
||||
hours?: number;
|
||||
days?: number;
|
||||
};
|
||||
}
|
||||
)._data;
|
||||
milliseconds =
|
||||
(dd.milliseconds || 0) +
|
||||
(dd.seconds || 0) * 1000 +
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ describe('InputDurationDirective', () => {
|
|||
return 0;
|
||||
});
|
||||
|
||||
mockMsToStringPipe.transform.and.callFake((ms: number) => {
|
||||
mockMsToStringPipe.transform.and.callFake((ms: number | null | undefined) => {
|
||||
if (ms === 7380000) return '2h 3m';
|
||||
if (ms === 7200000) return '2h';
|
||||
if (ms === 3600000) return '1h';
|
||||
|
|
@ -200,7 +200,7 @@ describe('InputDurationDirective', () => {
|
|||
return 0;
|
||||
});
|
||||
|
||||
mockMsToStringPipe.transform.and.callFake((ms: number) => {
|
||||
mockMsToStringPipe.transform.and.callFake((ms: number | null | undefined) => {
|
||||
if (ms === 3600000) return '1h';
|
||||
if (ms === 1800000) return '30m';
|
||||
if (ms === 5400000) return '1h 30m';
|
||||
|
|
|
|||
|
|
@ -88,11 +88,11 @@ export class InputDurationDirective implements ControlValueAccessor, Validator,
|
|||
this._formatDisplayValue();
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
registerOnChange(fn: (value: number | null) => void): void {
|
||||
this._onChange = fn;
|
||||
}
|
||||
|
||||
registerOnTouched(fn: any): void {
|
||||
registerOnTouched(fn: () => void): void {
|
||||
this._onTouched = fn;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
export const msToClockString = (
|
||||
value: any,
|
||||
value: number | null | undefined,
|
||||
showSeconds?: boolean,
|
||||
isHideEmptyPlaceholder?: boolean,
|
||||
): string => {
|
||||
|
|
@ -32,5 +32,6 @@ export const msToClockString = (
|
|||
|
||||
@Pipe({ name: 'msToClockString' })
|
||||
export class MsToClockStringPipe implements PipeTransform {
|
||||
transform: (value: any, ...args: any[]) => any = msToClockString;
|
||||
transform: (value: number | null | undefined, ...args: [boolean?, boolean?]) => string =
|
||||
msToClockString;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
export const msToMinuteClockString = (value: any): string => {
|
||||
export const msToMinuteClockString = (value: number | null | undefined): string => {
|
||||
const totalMs = Number(value) || 0;
|
||||
const totalSeconds = Math.floor(Math.abs(totalMs) / 1000);
|
||||
const totalMinutes = Math.floor(totalSeconds / 60);
|
||||
|
|
@ -15,5 +15,6 @@ export const msToMinuteClockString = (value: any): string => {
|
|||
|
||||
@Pipe({ name: 'msToMinuteClockString' })
|
||||
export class MsToMinuteClockStringPipe implements PipeTransform {
|
||||
transform: (value: any, ...args: any[]) => any = msToMinuteClockString;
|
||||
transform: (value: number | null | undefined, ...args: unknown[]) => string =
|
||||
msToMinuteClockString;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,15 +5,16 @@ const M = S * 60;
|
|||
const H = M * 60;
|
||||
|
||||
export const msToString = (
|
||||
value: any,
|
||||
value: number | null | undefined,
|
||||
isShowSeconds?: boolean,
|
||||
isHideEmptyPlaceholder?: boolean,
|
||||
): string => {
|
||||
const hours = Math.floor(value / H);
|
||||
const numValue = Number(value) || 0;
|
||||
const hours = Math.floor(numValue / H);
|
||||
// prettier-ignore
|
||||
const minutes = Math.floor((value - (hours * H)) / M);
|
||||
const minutes = Math.floor((numValue - (hours * H)) / M);
|
||||
// prettier-ignore
|
||||
const seconds = isShowSeconds ? Math.floor((value - (hours * H) - (minutes * M)) / S) : 0;
|
||||
const seconds = isShowSeconds ? Math.floor((numValue - (hours * H) - (minutes * M)) / S) : 0;
|
||||
|
||||
const parsed =
|
||||
// ((+md.days() > 0) ? (md.days() + 'd ') : '')
|
||||
|
|
@ -31,7 +32,7 @@ export const msToString = (
|
|||
@Pipe({ name: 'msToString' })
|
||||
export class MsToStringPipe implements PipeTransform {
|
||||
transform: (
|
||||
value: any,
|
||||
value: number | null | undefined,
|
||||
isShowSeconds?: boolean,
|
||||
isHideEmptyPlaceholder?: boolean,
|
||||
) => string = msToString;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
export const stringToMs = (strValue: string, args?: any): number => {
|
||||
export const stringToMs = (strValue: string, args?: unknown): number => {
|
||||
if (!strValue) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -87,5 +87,5 @@ export const stringToMs = (strValue: string, args?: any): number => {
|
|||
|
||||
@Pipe({ name: 'stringToMs' })
|
||||
export class StringToMsPipe implements PipeTransform {
|
||||
transform: (value: any, ...args: any[]) => any = stringToMs;
|
||||
transform: (value: string, ...args: unknown[]) => number = stringToMs;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { humanizeTimestamp } from '../../util/humanize-timestamp';
|
|||
export class HumanizeTimestampPipe implements PipeTransform {
|
||||
private translateService = inject(TranslateService);
|
||||
|
||||
transform(value: any): any {
|
||||
transform(value: number | Date | string): string {
|
||||
return humanizeTimestamp(value, this.translateService);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,12 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||
pure: false,
|
||||
})
|
||||
export class KeysPipe implements PipeTransform {
|
||||
transform(value: any, sort: any, filterOutKeys?: any): any {
|
||||
if (value === Object(value)) {
|
||||
transform(
|
||||
value: Record<string, unknown> | null | undefined,
|
||||
sort: boolean | 'reverse',
|
||||
filterOutKeys?: string | string[],
|
||||
): string[] | null {
|
||||
if (value && value === Object(value)) {
|
||||
const keys = Object.keys(value);
|
||||
|
||||
if (typeof filterOutKeys === 'string') {
|
||||
|
|
@ -14,6 +18,13 @@ export class KeysPipe implements PipeTransform {
|
|||
if (index > -1) {
|
||||
keys.splice(index, 1);
|
||||
}
|
||||
} else if (Array.isArray(filterOutKeys)) {
|
||||
filterOutKeys.forEach((key) => {
|
||||
const index = keys.indexOf(key);
|
||||
if (index > -1) {
|
||||
keys.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (sort) {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ import { formatDate } from '../../util/format-date';
|
|||
|
||||
@Pipe({ name: 'momentFormat' })
|
||||
export class MomentFormatPipe implements PipeTransform {
|
||||
transform(value: any, args: any): any {
|
||||
transform(
|
||||
value: number | Date | string | null | undefined,
|
||||
args: string | null | undefined,
|
||||
): string | null {
|
||||
if (value && args) {
|
||||
const result = formatDate(value, args);
|
||||
return result || null;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const MAP = [
|
|||
|
||||
@Pipe({ name: 'numberToMonth' })
|
||||
export class NumberToMonthPipe implements PipeTransform {
|
||||
transform(value: any, args?: any): any {
|
||||
return MAP[parseInt(value, 10) - 1];
|
||||
transform(value: number | string, args?: unknown): string | undefined {
|
||||
return MAP[parseInt(String(value), 10) - 1];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,18 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||
|
||||
@Pipe({ name: 'sort' })
|
||||
export class SortPipe implements PipeTransform {
|
||||
transform(array: any[], field: string, reverse: boolean = false): any[] {
|
||||
transform<T extends Record<string, unknown>>(
|
||||
array: T[],
|
||||
field: keyof T,
|
||||
reverse: boolean = false,
|
||||
): T[] {
|
||||
const f = reverse ? -1 : 1;
|
||||
|
||||
if (!Array.isArray(array)) {
|
||||
return array;
|
||||
}
|
||||
const arr = [...array];
|
||||
arr.sort((a: any, b: any) => {
|
||||
arr.sort((a: T, b: T) => {
|
||||
if (a[field] < b[field]) {
|
||||
return -1 * f;
|
||||
} else if (a[field] > b[field]) {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@ import { Pipe, PipeTransform } from '@angular/core';
|
|||
pure: false,
|
||||
})
|
||||
export class ToArrayPipe implements PipeTransform {
|
||||
transform(obj: any, filterOutKeys?: any): any {
|
||||
if (obj === Object(obj)) {
|
||||
transform(
|
||||
obj: Record<string, unknown> | null | undefined,
|
||||
filterOutKeys?: string | string[],
|
||||
): Array<{ key: string; value: unknown }> | null {
|
||||
if (obj && obj === Object(obj)) {
|
||||
const keys = Object.keys(obj);
|
||||
|
||||
if (typeof filterOutKeys === 'string') {
|
||||
|
|
@ -14,8 +17,15 @@ export class ToArrayPipe implements PipeTransform {
|
|||
if (index > -1) {
|
||||
keys.splice(index, 1);
|
||||
}
|
||||
} else if (Array.isArray(filterOutKeys)) {
|
||||
filterOutKeys.forEach((key) => {
|
||||
const index = keys.indexOf(key);
|
||||
if (index > -1) {
|
||||
keys.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
const newArray: any[] = [];
|
||||
const newArray: Array<{ key: string; value: unknown }> = [];
|
||||
keys.forEach((key) => {
|
||||
newArray.push({
|
||||
key,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn } from '@angular
|
|||
|
||||
import { maxValidator } from './max.validator';
|
||||
|
||||
const MAX_VALIDATOR: any = {
|
||||
const MAX_VALIDATOR = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => MaxDirective),
|
||||
multi: true,
|
||||
|
|
@ -44,9 +44,11 @@ export class MaxDirective implements Validator, OnInit, OnChanges {
|
|||
}
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): { [key: string]: any } | null {
|
||||
validate(c: AbstractControl): { [key: string]: unknown } | null {
|
||||
if (this._validator) {
|
||||
return this._validator(c) as { [key: string]: any };
|
||||
return this._validator(c) as {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { AbstractControl, ValidatorFn, Validators } from '@angular/forms';
|
||||
|
||||
export const maxValidator = (max: number): ValidatorFn => {
|
||||
return (control: AbstractControl): { [key: string]: any } | null => {
|
||||
return (
|
||||
control: AbstractControl,
|
||||
): {
|
||||
[key: string]: unknown;
|
||||
} | null => {
|
||||
if (!max || Validators.required(control)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn } from '@angular
|
|||
|
||||
import { minValidator } from './min.validator';
|
||||
|
||||
const MIN_VALIDATOR: any = {
|
||||
const MIN_VALIDATOR = {
|
||||
provide: NG_VALIDATORS,
|
||||
useExisting: forwardRef(() => MinDirective),
|
||||
multi: true,
|
||||
|
|
@ -44,9 +44,11 @@ export class MinDirective implements Validator, OnInit, OnChanges {
|
|||
}
|
||||
}
|
||||
|
||||
validate(c: AbstractControl): { [key: string]: any } | null {
|
||||
validate(c: AbstractControl): { [key: string]: unknown } | null {
|
||||
if (this._validator) {
|
||||
return this._validator(c) as { [key: string]: any };
|
||||
return this._validator(c) as {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { AbstractControl, ValidatorFn, Validators } from '@angular/forms';
|
||||
|
||||
export const minValidator = (min: number): ValidatorFn => {
|
||||
return (control: AbstractControl): { [key: string]: any } | null => {
|
||||
return (
|
||||
control: AbstractControl,
|
||||
): {
|
||||
[key: string]: unknown;
|
||||
} | null => {
|
||||
if (!min || Validators.required(control)) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ const NUMBER_OF_ACTIONS_TO_SAVE = 30;
|
|||
const actionLog: string[] = [];
|
||||
let beforeLastErrorLog: string[] = [];
|
||||
|
||||
export const actionLogger = (action: any): void => {
|
||||
export const actionLogger = (action: { type: string; [key: string]: unknown }): void => {
|
||||
if (action.type.indexOf('@ngrx') === 0) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { Dictionary } from '@ngrx/entity';
|
||||
|
||||
export const arrayToDictionary = <T>(arr: T[]): Dictionary<T> => {
|
||||
export const arrayToDictionary = <T extends { id: string | number }>(
|
||||
arr: T[],
|
||||
): Dictionary<T> => {
|
||||
return arr.reduce(
|
||||
(acc: any, sc): Dictionary<unknown> => ({
|
||||
(acc: Dictionary<T>, sc): Dictionary<T> => ({
|
||||
...acc,
|
||||
[(sc as any).id]: sc,
|
||||
[sc.id]: sc,
|
||||
}),
|
||||
{},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export const dedupeByKey = (arr: any[], key: string): any[] => {
|
||||
export const dedupeByKey = <T>(arr: T[], key: keyof T): T[] => {
|
||||
const temp = arr.map((el) => el[key]);
|
||||
return arr.filter((el, i) => temp.indexOf(el[key]) === i);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { Log } from '../core/log';
|
|||
|
||||
let isShowAlert = true;
|
||||
|
||||
export const devError = (errStr: any): void => {
|
||||
export const devError = (errStr: string | Error | unknown): void => {
|
||||
if (environment.production) {
|
||||
Log.err(errStr);
|
||||
// TODO add super simple snack message if possible
|
||||
|
|
@ -13,7 +13,7 @@ export const devError = (errStr: any): void => {
|
|||
isShowAlert = false;
|
||||
}
|
||||
if (confirm(`Throw an error for error? ––– ${errStr}`)) {
|
||||
throw new Error(errStr);
|
||||
throw new Error(errStr as string);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { isObject } from './is-object';
|
||||
|
||||
export const distinctUntilChangedObject = (a: any, b: any): boolean => {
|
||||
export const distinctUntilChangedObject = <T>(a: T, b: T): boolean => {
|
||||
if ((isObject(a) && isObject(b)) || (Array.isArray(a) && Array.isArray(b))) {
|
||||
return JSON.stringify(a) === JSON.stringify(b);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export const exists = <T>(v: any): T | never => {
|
||||
export const exists = <T>(v: T | null | undefined): T | never => {
|
||||
if (!v) {
|
||||
throw new Error('Value is ' + v);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
import { Dictionary, EntityState } from '@ngrx/entity';
|
||||
import { arrayToDictionary } from './array-to-dictionary';
|
||||
|
||||
export const fakeEntityStateFromArray = <T>(items: Partial<T>[]): EntityState<T> => {
|
||||
const dict = arrayToDictionary(items) as Dictionary<T>;
|
||||
const ids = items.map((item) => (item as any).id);
|
||||
export const fakeEntityStateFromArray = <T extends { id: string | number }>(
|
||||
items: Partial<T>[],
|
||||
): EntityState<T> => {
|
||||
const dict = arrayToDictionary(items as T[]) as Dictionary<T>;
|
||||
const ids = items.map((item) => (item as T).id) as string[] | number[];
|
||||
return {
|
||||
entities: dict,
|
||||
ids,
|
||||
|
|
@ -11,7 +13,7 @@ export const fakeEntityStateFromArray = <T>(items: Partial<T>[]): EntityState<T>
|
|||
};
|
||||
|
||||
export const fakeEntityStateFromNumbersArray = <T>(...nrs: number[]): EntityState<T> => {
|
||||
const items: any = nrs.map((nr) => ({ id: '_' + nr }));
|
||||
const items = nrs.map((nr) => ({ id: '_' + nr }));
|
||||
|
||||
const dict = arrayToDictionary(items) as Dictionary<T>;
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,21 @@
|
|||
import { isObject } from './is-object';
|
||||
import { HANDLED_ERROR_PROP_STR } from '../app.constants';
|
||||
|
||||
export const getErrorTxt = (err: any): string => {
|
||||
if (err && isObject(err.error)) {
|
||||
export const getErrorTxt = (err: unknown): string => {
|
||||
if (err && isObject((err as any).error)) {
|
||||
return (
|
||||
err.error.message ||
|
||||
err.error.name ||
|
||||
(err as any).error.message ||
|
||||
(err as any).error.name ||
|
||||
// for ngx translate...
|
||||
(isObject(err.error.error) ? err.error.error.toString() : err.error) ||
|
||||
err.error
|
||||
(isObject((err as any).error.error)
|
||||
? (err as any).error.error.toString()
|
||||
: (err as any).error) ||
|
||||
(err as any).error
|
||||
);
|
||||
} else if (err && err[HANDLED_ERROR_PROP_STR]) {
|
||||
return err[HANDLED_ERROR_PROP_STR];
|
||||
} else if (err && err.toString) {
|
||||
return err.toString();
|
||||
} else if (err && (err as any)[HANDLED_ERROR_PROP_STR]) {
|
||||
return (err as any)[HANDLED_ERROR_PROP_STR];
|
||||
} else if (err && (err as any).toString) {
|
||||
return (err as any).toString();
|
||||
} else if (typeof err === 'string') {
|
||||
return err;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { IS_ELECTRON } from '../app.constants';
|
|||
import { devError } from './dev-error';
|
||||
import { Log } from '../core/log';
|
||||
|
||||
const handlerMap: { [key: string]: Observable<any> } = {};
|
||||
const handlerMap: { [key: string]: Observable<unknown[]> } = {};
|
||||
|
||||
export const ipcEvent$ = (evName: string): Observable<unknown[]> => {
|
||||
if (!IS_ELECTRON) {
|
||||
|
|
@ -18,7 +18,7 @@ export const ipcEvent$ = (evName: string): Observable<unknown[]> => {
|
|||
}
|
||||
handlerMap[evName] = subject;
|
||||
|
||||
const handler: (...args: any[]) => void = (...args): void => {
|
||||
const handler: (...args: unknown[]) => void = (...args): void => {
|
||||
Log.log('ipcEvent$ trigger', evName);
|
||||
subject.next([...args]);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
export const isObject = (obj: any): boolean => obj === Object(obj);
|
||||
export const isObject = (obj: unknown): obj is object => obj === Object(obj);
|
||||
|
|
|
|||
|
|
@ -6,9 +6,11 @@ export const observeWidth = (target: HTMLElement): Observable<number> => {
|
|||
// eslint-disable-next-line
|
||||
if ((window as any).ResizeObserver) {
|
||||
// eslint-disable-next-line
|
||||
const resizeObserver = new (window as any).ResizeObserver((entries: any[]) => {
|
||||
observer.next(entries[0].contentRect.width);
|
||||
});
|
||||
const resizeObserver = new (window as any).ResizeObserver(
|
||||
(entries: ResizeObserverEntry[]) => {
|
||||
observer.next(entries[0].contentRect.width);
|
||||
},
|
||||
);
|
||||
resizeObserver.observe(target);
|
||||
return () => {
|
||||
resizeObserver.unobserve(target);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export const uniqueByProp = <T>(array: T[], propName: keyof T): T[] => {
|
||||
const r: T[] = [];
|
||||
const allCompareKeys: string[] = [];
|
||||
array.forEach((v: any) => {
|
||||
const allCompareKeys: unknown[] = [];
|
||||
array.forEach((v: T) => {
|
||||
const compareProp = v[propName];
|
||||
if (!allCompareKeys.includes(compareProp)) {
|
||||
r.push(v);
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ export const updateAllInDictionary = <T>(
|
|||
oldD: Dictionary<T>,
|
||||
changes: Partial<T>,
|
||||
): Dictionary<T> => {
|
||||
const newD: any = {};
|
||||
const newD: Dictionary<T> = {};
|
||||
const ids = Object.keys(oldD);
|
||||
|
||||
ids.forEach((id: string) => {
|
||||
newD[id] = {
|
||||
...oldD[id],
|
||||
...changes,
|
||||
};
|
||||
} as T;
|
||||
});
|
||||
|
||||
return newD;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
export const watchObject = <T extends object>(
|
||||
obj: T,
|
||||
onChange: (prop: string, value: any) => void,
|
||||
onChange: (prop: string, value: unknown) => void,
|
||||
): T =>
|
||||
new Proxy(obj, {
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
|
|
|
|||
15
src/test.ts
15
src/test.ts
|
|
@ -15,25 +15,30 @@ beforeAll(() => {
|
|||
// Mock browser dialogs globally for tests
|
||||
// We need to handle tests that try to spy on alert/confirm after we've already mocked them
|
||||
// First check if alert/confirm are already spies (from previous test runs)
|
||||
if (!(window.alert as any).and) {
|
||||
if (!(window.alert as jasmine.Spy).and) {
|
||||
window.alert = jasmine.createSpy('alert');
|
||||
}
|
||||
if (!(window.confirm as any).and) {
|
||||
if (!(window.confirm as jasmine.Spy).and) {
|
||||
window.confirm = jasmine.createSpy('confirm').and.returnValue(true);
|
||||
}
|
||||
|
||||
// Configure the TestBed providers globally
|
||||
const originalConfigureTestingModule = TestBed.configureTestingModule;
|
||||
TestBed.configureTestingModule = function (moduleDef: any) {
|
||||
TestBed.configureTestingModule = function (
|
||||
moduleDef: Parameters<typeof originalConfigureTestingModule>[0],
|
||||
) {
|
||||
if (!moduleDef.providers) {
|
||||
moduleDef.providers = [];
|
||||
}
|
||||
|
||||
// Add zoneless change detection provider if not already present
|
||||
const hasZonelessProvider = moduleDef.providers.some(
|
||||
(p: any) =>
|
||||
(p: unknown) =>
|
||||
p === provideExperimentalZonelessChangeDetection ||
|
||||
(p && p.provide === provideExperimentalZonelessChangeDetection),
|
||||
(p &&
|
||||
typeof p === 'object' &&
|
||||
'provide' in p &&
|
||||
p.provide === provideExperimentalZonelessChangeDetection),
|
||||
);
|
||||
|
||||
if (!hasZonelessProvider) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue