From 1d4ca29304947e76d323498e04ebecab5aa5bc7c Mon Sep 17 00:00:00 2001 From: trydofor Date: Fri, 20 Dec 2024 17:24:08 +0800 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20typesafe=20use=20of=20any?= =?UTF-8?q?=20and=20object=20#130?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/sharp-dingos-explode.md | 6 ++++++ layers/common/composables/UseApiRoute.ts | 6 ++---- layers/common/tests/safe-converter.test.ts | 18 ++++++------------ layers/common/types/common.global.d.ts | 12 ++++++++++++ layers/common/utils/element-action.ts | 12 ++++-------- layers/common/utils/safe-converter.ts | 12 ++++-------- layers/common/utils/typed-fetcher.ts | 7 +++---- layers/mobile/tests/ionic-fetcher.test.ts | 12 ++++-------- layers/mobile/utils/ionic-fetcher.ts | 3 +-- layers/mobile/utils/ionic-validator.ts | 3 +-- 10 files changed, 43 insertions(+), 48 deletions(-) create mode 100644 .changeset/sharp-dingos-explode.md diff --git a/.changeset/sharp-dingos-explode.md b/.changeset/sharp-dingos-explode.md new file mode 100644 index 0000000..8a76184 --- /dev/null +++ b/.changeset/sharp-dingos-explode.md @@ -0,0 +1,6 @@ +--- +"@fessional/razor-common": patch +"@fessional/razor-mobile": patch +--- + +♻️ SafeAny, SafeObj, TypedFetch status function diff --git a/layers/common/composables/UseApiRoute.ts b/layers/common/composables/UseApiRoute.ts index f115881..cd79f10 100644 --- a/layers/common/composables/UseApiRoute.ts +++ b/layers/common/composables/UseApiRoute.ts @@ -10,15 +10,13 @@ export function useApiRoute() { const prefix = useRuntimeConfig().public.apiRoute; return { url: (uri: string) => prefix + uri, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - get: (uri: string, query?: Record) => $fetch>( + get: (uri: string, query?: SafeObj) => $fetch>( prefix + uri, { method: 'get', query, }), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - post: (uri: string, body?: Record | URLSearchParams | FormData, query?: Record) => $fetch>( + post: (uri: string, body?: SafeObj | URLSearchParams | FormData, query?: SafeObj) => $fetch>( prefix + uri, { method: 'post', diff --git a/layers/common/tests/safe-converter.test.ts b/layers/common/tests/safe-converter.test.ts index 5dcf0f6..01d521b 100644 --- a/layers/common/tests/safe-converter.test.ts +++ b/layers/common/tests/safe-converter.test.ts @@ -25,15 +25,13 @@ describe('safeConvert', () => { }); it('should break after one function evaluation if once is true', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const nestedFunction: any = () => () => 10; + const nestedFunction: SafeAny = () => () => 10; const result = safeConvert(nestedFunction, 'default', value => value > 5 ? 'valid' : null, true); expect(result).toBe('default'); }); it('should evaluate nested functions until a value is returned when once is false', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const nestedFunction: any = () => () => 10; + const nestedFunction: SafeAny = () => () => 10; const result = safeConvert(nestedFunction, 'default', value => value > 5 ? 'valid' : null); expect(result).toBe('valid'); }); @@ -138,8 +136,7 @@ describe('safeBigint', () => { expect(safeBigint(null)).toBe(0n); expect(safeBigint(undefined)).toBe(0n); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(safeBigint({} as any)).toBe(0n); + expect(safeBigint({} as SafeAny)).toBe(0n); expect(safeBigint(NaN)).toBe(0n); }); @@ -203,10 +200,8 @@ describe('safeBoolean', () => { expect(safeBoolean(undefined, false)).toBe(false); expect(safeBoolean(NaN, false)).toBe(false); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(safeBoolean({} as any, true)).toBe(true); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - expect(safeBoolean({} as any, false)).toBe(false); + expect(safeBoolean({} as SafeAny, true)).toBe(true); + expect(safeBoolean({} as SafeAny, false)).toBe(false); }); }); @@ -439,8 +434,7 @@ describe('numberKey;', () => { const d3 = 0.1 + 0.2; it('number key of object', () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const obj = { 1: '1', 0.3: '0.3' } as any; + const obj = { 1: '1', 0.3: '0.3' } as SafeAny; expect(obj[1]).toBe('1'); expect(obj['1']).toBe('1'); expect(obj[d3]).toBe(undefined); diff --git a/layers/common/types/common.global.d.ts b/layers/common/types/common.global.d.ts index 3773a5c..d6b804a 100644 --- a/layers/common/types/common.global.d.ts +++ b/layers/common/types/common.global.d.ts @@ -28,6 +28,18 @@ */ type OrElse = NonNullable | D; + /** + * typesafe use of `any` to reduce eslint comments + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + type SafeAny = any; + + /** + * typesafe use of `object` to reduce eslint comments + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + type SafeObj = Record; + /** * short string value of true or false */ diff --git a/layers/common/utils/element-action.ts b/layers/common/utils/element-action.ts index 921641b..566a302 100644 --- a/layers/common/utils/element-action.ts +++ b/layers/common/utils/element-action.ts @@ -4,8 +4,7 @@ * @param element the element Ref or id * @returns the element */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function selectElement(element: Ref | string): T { +export function selectElement(element: Ref | string): T { return typeof element === 'string' ? document.getElementById(element) : element.value.$el; @@ -16,11 +15,9 @@ export function selectElement(element: Ref | string): T { * @param element the element Ref or id * @param method the focus method name, if not found, use focus() */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function focusElement(element: Ref | string, method = 'setFocus') { +export function focusElement(element: Ref | string, method = 'setFocus') { return nextTick(() => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const ele = selectElement(element) as any; + const ele = selectElement(element) as SafeAny; if (typeof ele[method] === 'function') { ele[method](); @@ -38,8 +35,7 @@ export function focusElement(element: Ref | string, method = 'setFocus') { * @param element the element Ref or id * @param vertical vertical alignment of the element */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function scrollElement(element: Ref | string, vertical: 'center' | 'end' | 'nearest' | 'start' = 'center') { +export function scrollElement(element: Ref | string, vertical: 'center' | 'end' | 'nearest' | 'start' = 'center') { return nextTick(() => { const ele = selectElement(element); ele.scrollIntoView({ behavior: 'smooth', block: vertical }); diff --git a/layers/common/utils/safe-converter.ts b/layers/common/utils/safe-converter.ts index a2066b6..bcc7ba8 100644 --- a/layers/common/utils/safe-converter.ts +++ b/layers/common/utils/safe-converter.ts @@ -57,8 +57,7 @@ export function isVoid(arg: unknown) { * @param defaults default value if null/undefined * @returns */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function safeString(valOrFun: any, defaults: string = ''): string { +export function safeString(valOrFun: SafeAny, defaults: string = ''): string { return safeConvert(valOrFun, defaults, (value) => { switch (typeof value) { case 'string': @@ -282,8 +281,7 @@ export function safeValue(valOrFun: Maybe | (() => Maybe * @param defaults default value if null/undefined * @returns */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function safeValues(valOrFun: any, defaults: T[] = []): T[] { +export function safeValues(valOrFun: SafeAny, defaults: T[] = []): T[] { return safeConvert(valOrFun, defaults, Object.values); } @@ -294,8 +292,7 @@ export function safeValues(valOrFun: any, defaults: T[] = []): T[] { * @param defaults default value if null/undefined * @returns */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function safeKeys(valOrFun: any, defaults: string[] = []): string[] { +export function safeKeys(valOrFun: SafeAny, defaults: string[] = []): string[] { return safeConvert(valOrFun, defaults, Object.keys); } @@ -306,8 +303,7 @@ export function safeKeys(valOrFun: any, defaults: string[] = []): string[] { * @param defaults default value if null/undefined * @returns */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function safeEntries(valOrFun: any, defaults: [string, T][] = []): [string, T][] { +export function safeEntries(valOrFun: SafeAny, defaults: [string, T][] = []): [string, T][] { return safeConvert(valOrFun, defaults, Object.entries); } diff --git a/layers/common/utils/typed-fetcher.ts b/layers/common/utils/typed-fetcher.ts index ecd084c..9bc8c96 100644 --- a/layers/common/utils/typed-fetcher.ts +++ b/layers/common/utils/typed-fetcher.ts @@ -18,8 +18,8 @@ export type TypedFetchOptions = { * catch of try fetching * @param err any caught error */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - catches?: (err: any) => void; + + catches?: (err: SafeAny) => void; }; function _doLoading(status: LoadingStatus, loading?: Ref | ((status: LoadingStatus) => void)): void { @@ -39,8 +39,7 @@ function _doResult(failure?: (message?: string, code?: string) => void, resul return result; } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function _doError(err: any, catches?: (err: any) => void): void { +function _doError(err: SafeAny, catches?: (err: SafeAny) => void): void { if (catches) { catches(err); } diff --git a/layers/mobile/tests/ionic-fetcher.test.ts b/layers/mobile/tests/ionic-fetcher.test.ts index bfe541c..2b18650 100644 --- a/layers/mobile/tests/ionic-fetcher.test.ts +++ b/layers/mobile/tests/ionic-fetcher.test.ts @@ -21,8 +21,7 @@ describe('ionicFetchingDataAsync', () => { it('should show loading while fetching and dismiss loading after fetching', async () => { const loadingUiMock = { present: vi.fn(), dismiss: vi.fn() }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (loadingController.create as any).mockResolvedValue(loadingUiMock); + (loadingController.create as SafeAny).mockResolvedValue(loadingUiMock); const fetching = Promise.resolve({ success: true, data: 'test-data' } as DataResult); @@ -41,8 +40,7 @@ describe('ionicFetchingDataAsync', () => { it('should handle failure by showing an alert when fetching fails', async () => { const alertMock = { present: vi.fn() }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (alertController.create as any).mockResolvedValue(alertMock); + (alertController.create as SafeAny).mockResolvedValue(alertMock); const fetching = Promise.resolve({ success: false, message: 'Fetch error', code: '404' } as DataResult); @@ -58,8 +56,7 @@ describe('ionicFetchingDataAsync', () => { it('should handle catches by showing an alert when an error is thrown', async () => { const alertMock = { present: vi.fn() }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (alertController.create as any).mockResolvedValue(alertMock); + (alertController.create as SafeAny).mockResolvedValue(alertMock); const fetching = Promise.reject(new Error('Network error')); @@ -85,8 +82,7 @@ describe('ionicFetchingDataAsync', () => { it('should call alertFailure when fetching fails and loading Ref is provided', async () => { const alertMock = { present: vi.fn() }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (alertController.create as any).mockResolvedValue(alertMock); + (alertController.create as SafeAny).mockResolvedValue(alertMock); const loadingRef = ref(true); const fetching = Promise.resolve({ success: false, message: 'Fetch error', code: '500' } as DataResult); diff --git a/layers/mobile/utils/ionic-fetcher.ts b/layers/mobile/utils/ionic-fetcher.ts index f758612..064aade 100644 --- a/layers/mobile/utils/ionic-fetcher.ts +++ b/layers/mobile/utils/ionic-fetcher.ts @@ -11,8 +11,7 @@ function _failure(message?: string, code?: string) { }); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -function _catches(err: any) { +function _catches(err: SafeAny) { alertController.create({ header: 'Failed to fetch data', message: err.message || 'Network error', diff --git a/layers/mobile/utils/ionic-validator.ts b/layers/mobile/utils/ionic-validator.ts index 4dfbd39..5222b8a 100644 --- a/layers/mobile/utils/ionic-validator.ts +++ b/layers/mobile/utils/ionic-validator.ts @@ -24,8 +24,7 @@ export type IonInputEvent = CustomEvent & { detail: { value?: string | null } }; export function ionicValidateInput( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - inputRef: Ref, + inputRef: Ref, checkFun: RegExp | ((value: string, event?: IonInputEvent) => boolean), modelRef?: Ref, ): (ev?: IonInputEvent | string | null) => boolean | null {