Skip to content

Commit b43778e

Browse files
committed
refactor: replace all any types with proper TypeScript types
- Add ICacheOptions interface for cache configuration options - Add ICacheMeta interface for cache entry metadata - Update ICacheEntry interface to use generic types - Replace `any[]` with `unknown[]` for function arguments in key strategies - Replace `any` with `unknown` for cache content parameters across all storage implementations - Add IMultiCacheKeyStrategy interface for multi-cache decorator - Add proper type guards in ExpirationStrategy - Update all storage implementations (Memory, FS JSON, LRU, LRU-Redis, NodeCache, Redis, RedisIO) with proper types - Fix test files to use `unknown` instead of `any` for error handling and arguments This improves type safety across the entire codebase by eliminating all `any` types, making the library more robust and easier to maintain.
1 parent 01d0a3d commit b43778e

17 files changed

Lines changed: 202 additions & 125 deletions

storages/lru-redis/src/LRUWithRedisStorage.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import LRU from 'lru-cache';
44
import * as Redis from 'ioredis';
55

66
export class LRUWithRedisStorage implements IAsynchronousCacheType {
7-
private myCache: LRU<string, any>;
7+
private myCache: LRU<string, unknown>;
88

99
/** maxAge and ttl in seconds! */
10-
private options: LRU.Options<string, any>;
10+
private options: LRU.Options<string, unknown>;
1111

12-
constructor(options: LRU.Options<string, any>, private redis: () => Redis.Redis) {
12+
constructor(options: LRU.Options<string, unknown>, private redis: () => Redis.Redis) {
1313
this.options = {
1414
max: 500,
1515
maxAge: 86400,
@@ -23,15 +23,15 @@ export class LRUWithRedisStorage implements IAsynchronousCacheType {
2323

2424
public async getItem<T>(key: string): Promise<T | undefined> {
2525
// check local cache
26-
let localCache = this.myCache.get(key);
26+
let localCache: unknown = this.myCache.get(key);
2727

2828
if (localCache === undefined) {
2929
// check central cache
30-
localCache = await this.redis().get(key);
30+
const redisValue = await this.redis().get(key);
3131

32-
if (localCache !== undefined) {
32+
if (redisValue !== null) {
3333
try {
34-
localCache = JSON.parse(localCache);
34+
localCache = JSON.parse(redisValue);
3535
} catch (err) {
3636
console.error('lru redis cache failed parsing data', err);
3737
localCache = undefined;
@@ -41,11 +41,11 @@ export class LRUWithRedisStorage implements IAsynchronousCacheType {
4141
}
4242
}
4343

44-
return localCache ?? undefined;
44+
return localCache as T | undefined;
4545
}
4646

4747
/** ttl in seconds! */
48-
public async setItem(key: string, content: any, options?: { ttl?: number }): Promise<void> {
48+
public async setItem(key: string, content: unknown, options?: { ttl?: number }): Promise<void> {
4949
this.myCache.set(key, content);
5050
if (this.options?.maxAge) {
5151
await this.redis().setex(key, options?.ttl || this.options.maxAge, JSON.stringify(content));

storages/lru/src/LRUStorage.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,32 @@ import type { IMultiSynchronousCacheType, ISynchronousCacheType } from '@hokify/
33
import LRU from 'lru-cache';
44

55
export class LRUStorage implements ISynchronousCacheType, IMultiSynchronousCacheType {
6-
myCache: LRU<string, any>;
6+
myCache: LRU<string, unknown>;
77

8-
constructor(/** maxAge in seconds! */ private options: LRU.Options<string, any>) {
8+
constructor(/** maxAge in seconds! */ private options: LRU.Options<string, unknown>) {
99
this.myCache = new LRU({
1010
...options,
1111
maxAge: options.maxAge ? options.maxAge * 1000 : undefined
1212
});
1313
}
1414

1515
getItems<T>(keys: string[]): { [key: string]: T | undefined } {
16-
return Object.fromEntries(keys.map(key => [key, this.myCache.get(key)]));
16+
return Object.fromEntries(keys.map(key => [key, this.myCache.get(key)])) as {
17+
[key: string]: T | undefined;
18+
};
1719
}
1820

19-
setItems(values: { key: string; content: any }[]): void {
21+
setItems(values: { key: string; content: unknown }[]): void {
2022
values.forEach(val => {
2123
this.myCache.set(val.key, val.content);
2224
});
2325
}
2426

2527
public getItem<T>(key: string): T | undefined {
26-
return this.myCache.get(key) || undefined;
28+
return this.myCache.get(key) as T | undefined;
2729
}
2830

29-
public setItem(key: string, content: any): void {
31+
public setItem(key: string, content: unknown): void {
3032
this.myCache.set(key, content);
3133
}
3234

storages/node-cache/src/node-cache.storage.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ export class NodeCacheStorage implements ISynchronousCacheType, IMultiSynchronou
1313
return this.myCache.mget(keys);
1414
}
1515

16-
setItems(values: { key: string; content: any }[]): void {
16+
setItems(values: { key: string; content: unknown }[]): void {
1717
this.myCache.mset(values.map(v => ({ key: v.key, val: v.content })));
1818
}
1919

2020
public getItem<T>(key: string): T | undefined {
2121
return this.myCache.get(key) || undefined;
2222
}
2323

24-
public setItem(key: string, content: any): void {
24+
public setItem(key: string, content: unknown): void {
2525
this.myCache.set(key, content);
2626
}
2727

storages/redis/src/redis.storage.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,35 @@ export class RedisStorage implements IAsynchronousCacheType {
1515
this.client = redis.createClient(this.redisOptions) as IRedisClient;
1616
}
1717

18-
public async getItem<T>(key: string): Promise<T> {
19-
const entry: any = await this.client.getAsync(key);
20-
let finalItem = entry;
18+
public async getItem<T>(key: string): Promise<T | undefined> {
19+
const entry: string | null = await this.client.getAsync(key);
20+
if (entry === null) {
21+
return undefined;
22+
}
23+
let finalItem: unknown = entry;
2124
try {
2225
finalItem = JSON.parse(entry);
2326
} catch (error) {
2427
/** ignore */
2528
}
26-
return finalItem || undefined;
29+
return finalItem as T | undefined;
2730
}
2831

29-
public async setItem(key: string, content: any): Promise<void> {
32+
public async setItem(key: string, content: unknown): Promise<void> {
33+
let stringContent: string;
34+
if (content === undefined) {
35+
await this.client.delAsync(key);
36+
return;
37+
}
3038
if (typeof content === 'object') {
31-
content = JSON.stringify(content);
32-
} else if (content === undefined) {
33-
return this.client.delAsync(key);
39+
stringContent = JSON.stringify(content);
40+
} else {
41+
stringContent = String(content);
3442
}
35-
return this.client.setAsync(key, content);
43+
await this.client.setAsync(key, stringContent);
3644
}
3745

3846
public async clear(): Promise<void> {
39-
return this.client.flushdbAsync();
47+
await this.client.flushdbAsync();
4048
}
4149
}

storages/redisio/src/redisio.storage.ts

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,27 @@ export class RedisIOStorage implements IAsynchronousCacheType, IMultiIAsynchrono
1818
}
1919

2020
async getItems<T>(keys: string[]): Promise<{ [key: string]: T | undefined }> {
21-
const mget = this.options.compress
22-
? await (this.redis() as any).mgetBuffer(...keys)
21+
const mget: (Buffer | string | null)[] = this.options.compress
22+
? await this.redis().mgetBuffer(...keys)
2323
: await this.redis().mget(...keys);
2424
const res = Object.fromEntries(
2525
await Promise.all(
26-
mget.map(async (entry: Buffer | string, i: number) => {
26+
mget.map(async (entry: Buffer | string | null, i: number) => {
2727
if (entry === null) {
2828
return [keys[i], undefined]; // value does not exist yet
2929
}
3030

3131
if (entry === '') {
32-
return [keys[i], null as any]; // value does exist, but is empty
32+
return [keys[i], null]; // value does exist, but is empty
3333
}
3434

35-
let finalItem: string =
35+
let finalItem: unknown =
3636
entry && this.options.compress
3737
? await this.uncompress(entry as Buffer)
3838
: (entry as string);
3939

4040
try {
41-
finalItem = finalItem && JSON.parse(finalItem);
41+
finalItem = finalItem && JSON.parse(finalItem as string);
4242
} catch (error) {
4343
/** ignore */
4444
}
@@ -62,7 +62,7 @@ export class RedisIOStorage implements IAsynchronousCacheType, IMultiIAsynchrono
6262
}
6363

6464
async setItems(
65-
values: { key: string; content: any }[],
65+
values: { key: string; content: unknown }[],
6666
options?: { ttl?: number }
6767
): Promise<void> {
6868
const redisPipeline = this.redis().pipeline();
@@ -102,17 +102,17 @@ export class RedisIOStorage implements IAsynchronousCacheType, IMultiIAsynchrono
102102
return undefined;
103103
}
104104
if (entry === '') {
105-
return null as any;
105+
return null as T; // value exists but is empty
106106
}
107-
let finalItem: string =
107+
let finalItem: unknown =
108108
entry && this.options.compress ? await this.uncompress(entry as Buffer) : (entry as string);
109109

110110
try {
111-
finalItem = JSON.parse(finalItem);
111+
finalItem = JSON.parse(finalItem as string);
112112
} catch (error) {
113113
/** ignore */
114114
}
115-
return finalItem as unknown as T;
115+
return finalItem as T;
116116
}
117117

118118
public async setItem(key: string, content: unknown, options?: { ttl?: number }): Promise<void> {
@@ -128,7 +128,7 @@ export class RedisIOStorage implements IAsynchronousCacheType, IMultiIAsynchrono
128128
}
129129

130130
const ttl = options?.ttl ?? this.options.maxAge;
131-
let savePromise: Promise<any>;
131+
let savePromise: Promise<'OK' | null>;
132132
if (ttl) {
133133
savePromise = this.redis().setex(key, ttl, content as Buffer | string);
134134
} else {

ts-cache/src/decorator/cache.decorator.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
import { JSONStringifyKeyStrategy } from '../strategy/key/json.stringify.strategy.js';
22
import { IAsyncKeyStrategy } from '../types/key.strategy.types.js';
3-
import { IAsynchronousCacheType, ISynchronousCacheType } from '../types/cache.types.js';
3+
import {
4+
IAsynchronousCacheType,
5+
ISynchronousCacheType,
6+
ICacheOptions
7+
} from '../types/cache.types.js';
48

59
const defaultKeyStrategy = new JSONStringifyKeyStrategy();
610

711
export function Cache(
812
cachingStrategy: IAsynchronousCacheType | ISynchronousCacheType,
9-
options?: any,
13+
options?: ICacheOptions,
1014
keyStrategy: IAsyncKeyStrategy = defaultKeyStrategy
1115
) {
1216
return function (
1317
// eslint-disable-next-line @typescript-eslint/ban-types
1418
target: Object & {
1519
__cache_decarator_pending_results?: {
16-
[key: string]: Promise<any> | undefined;
20+
[key: string]: Promise<unknown> | undefined;
1721
};
1822
},
1923
methodName: string,
@@ -22,7 +26,7 @@ export function Cache(
2226
const originalMethod = descriptor.value;
2327
const className = target.constructor.name;
2428

25-
descriptor.value = async function (...args: any[]) {
29+
descriptor.value = async function (...args: unknown[]) {
2630
const cacheKey = await keyStrategy.getKey(className, methodName, args);
2731

2832
const runMethod = async () => {
@@ -54,7 +58,7 @@ export function Cache(
5458
target.__cache_decarator_pending_results[cacheKey] = (async () => {
5559
try {
5660
try {
57-
const entry = await (cachingStrategy.getItem as any)(cacheKey);
61+
const entry = await cachingStrategy.getItem<unknown>(cacheKey);
5862
if (entry !== undefined) {
5963
return entry;
6064
}

ts-cache/src/decorator/multicache.decorator.ts

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
import { IMultiIAsynchronousCacheType, IMultiSynchronousCacheType } from '../types/cache.types.js';
22

3-
const defaultKeyStrategy = {
3+
/**
4+
* Key strategy interface for multi-cache operations
5+
*/
6+
export interface IMultiCacheKeyStrategy {
47
getKey(
58
className: string,
69
methodName: string,
7-
parameter: any,
8-
args: any,
10+
parameter: unknown,
11+
args: unknown[],
12+
phase: 'read' | 'write'
13+
): string | undefined;
14+
}
15+
16+
const defaultKeyStrategy: IMultiCacheKeyStrategy = {
17+
getKey(
18+
className: string,
19+
methodName: string,
20+
parameter: unknown,
21+
args: unknown[],
922
_phase: 'read' | 'write'
1023
): string | undefined {
1124
return `${className}:${methodName}:${JSON.stringify(parameter)}:${JSON.stringify(args)}`;
@@ -15,23 +28,19 @@ const defaultKeyStrategy = {
1528
export function MultiCache(
1629
cachingStrategies: (IMultiIAsynchronousCacheType | IMultiSynchronousCacheType)[],
1730
parameterIndex = 0,
18-
keyStrategy = defaultKeyStrategy
31+
keyStrategy: IMultiCacheKeyStrategy = defaultKeyStrategy
1932
) {
2033
return function (
2134
// eslint-disable-next-line @typescript-eslint/ban-types
22-
target: Object /* & {
23-
__cache_decarator_pending_results: {
24-
[key: string]: Promise<any> | undefined;
25-
};
26-
} */,
35+
target: Object,
2736
methodName: string,
2837
descriptor: PropertyDescriptor
2938
) {
3039
const originalMethod = descriptor.value;
3140
const className = target.constructor.name;
3241

33-
descriptor.value = async function (...args: any[]) {
34-
const runMethod = async (newSet: any[]) => {
42+
descriptor.value = async function (...args: unknown[]) {
43+
const runMethod = async (newSet: unknown[]) => {
3544
const newArgs = [...args];
3645
newArgs[parameterIndex] = newSet;
3746

@@ -50,18 +59,18 @@ export function MultiCache(
5059
return methodResult;
5160
};
5261

53-
const parameters = args[parameterIndex];
54-
const cacheKeys: (string | undefined)[] = parameters.map((parameter: any) =>
62+
const parameters = args[parameterIndex] as unknown[];
63+
const cacheKeys: (string | undefined)[] = parameters.map((parameter: unknown) =>
5564
keyStrategy.getKey(className, methodName, parameter, args, 'read')
5665
);
5766

58-
let result: any[] = [];
67+
let result: unknown[] = [];
5968
if (!process.env.DISABLE_CACHE_DECORATOR) {
6069
let currentCachingStrategy = 0;
6170
do {
6271
// console.log('cacheKeys', cacheKeys, currentCachingStrategy)
63-
const foundEntries = await (cachingStrategies[currentCachingStrategy] as any).getItems(
64-
cacheKeys.filter(key => key !== undefined)
72+
const foundEntries = await cachingStrategies[currentCachingStrategy].getItems<unknown>(
73+
cacheKeys.filter((key): key is string => key !== undefined)
6574
);
6675

6776
// console.log('foundEntries', foundEntries);
@@ -114,7 +123,7 @@ export function MultiCache(
114123
})
115124
.filter(k => k !== undefined);
116125

117-
const originalMethodResult: any[] = await runMethod(missingKeys);
126+
const originalMethodResult = (await runMethod(missingKeys)) as unknown[];
118127
if (originalMethodResult.length !== missingKeys.length) {
119128
throw new Error(
120129
`input and output has different size! input: ${cacheKeys.length}, returned ${originalMethodResult.length}`
@@ -136,7 +145,7 @@ export function MultiCache(
136145
content
137146
};
138147
})
139-
.filter(f => f !== undefined) as { key: string; content: any }[];
148+
.filter((f): f is { key: string; content: unknown } => f !== undefined);
140149

141150
// console.log('saveToCache', saveToCache);
142151

ts-cache/src/decorator/synccache.decorator.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
import { JSONStringifyKeyStrategy } from '../strategy/key/json.stringify.strategy.js';
22
import { ISyncKeyStrategy } from '../types/key.strategy.types.js';
3-
import { ISynchronousCacheType } from '../types/cache.types.js';
3+
import { ISynchronousCacheType, ICacheOptions } from '../types/cache.types.js';
44

55
const defaultKeyStrategy = new JSONStringifyKeyStrategy();
66

77
export function SyncCache(
88
cachingStrategy: ISynchronousCacheType,
9-
options?: any,
9+
options?: ICacheOptions,
1010
keyStrategy: ISyncKeyStrategy = defaultKeyStrategy
1111
) {
1212
// eslint-disable-next-line @typescript-eslint/ban-types
1313
return function (target: Object, methodName: string, descriptor: PropertyDescriptor) {
1414
const originalMethod = descriptor.value;
1515
const className = target.constructor.name;
1616

17-
descriptor.value = function (...args: any[]) {
17+
descriptor.value = function (...args: unknown[]) {
1818
const runMethod = () => {
1919
const methodResult = originalMethod.apply(this, args);
2020

0 commit comments

Comments
 (0)