Skip to content

Commit

Permalink
fix: proper generic usage to type deep properties
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurfiorette committed Jan 4, 2022
1 parent 8d5a90e commit ce2f597
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 43 deletions.
33 changes: 17 additions & 16 deletions src/cache/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { CacheInstance, CacheProperties } from './cache';
* @template D The type that the request body was
*/
export type CacheAxiosResponse<R = any, D = any> = AxiosResponse<R, D> & {
config: CacheRequestConfig<D>;
config: CacheRequestConfig<R, D>;

/** The id used for this request. if config specified an id, the id will be returned */
id: string;
Expand All @@ -24,9 +24,10 @@ export type CacheAxiosResponse<R = any, D = any> = AxiosResponse<R, D> & {
/**
* Options that can be overridden per request
*
* @template R The type returned by this response
* @template D The type for the request body
*/
export type CacheRequestConfig<D = any> = AxiosRequestConfig<D> & {
export type CacheRequestConfig<R = any, D = any> = AxiosRequestConfig<D> & {
/**
* An id for this request, if this request is used in cache, only the last request made
* with this id will be returned.
Expand All @@ -40,7 +41,7 @@ export type CacheRequestConfig<D = any> = AxiosRequestConfig<D> & {
*
* False means ignore everything about cache, for this request.
*/
cache?: false | Partial<CacheProperties>;
cache?: false | Partial<CacheProperties<R, D>>;
};

/**
Expand All @@ -58,7 +59,7 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
* @template D The type that the request body use
*/
<T = any, D = any, R = CacheAxiosResponse<T, D>>(
config: CacheRequestConfig<D>
config: CacheRequestConfig<T, D>
): Promise<R>;
/**
* @template T The type returned by this response
Expand All @@ -67,28 +68,28 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
*/
<T = any, D = any, R = CacheAxiosResponse<T, D>>(
url: string,
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;

defaults: AxiosDefaults<any> & {
cache: CacheProperties;
};

interceptors: {
request: AxiosInterceptorManager<CacheRequestConfig<any>>;
response: AxiosInterceptorManager<CacheAxiosResponse<never, any>>;
request: AxiosInterceptorManager<CacheRequestConfig<any, any>>;
response: AxiosInterceptorManager<CacheAxiosResponse<any, any>>;
};

/** @template D The type that the request body use */
getUri<D>(config?: CacheRequestConfig<D>): string;
getUri<D>(config?: CacheRequestConfig<any, D>): string;

/**
* @template T The type returned by this response
* @template R The custom response type that the request can return
* @template D The type that the request body use
*/
request<T = any, D = any, R = CacheAxiosResponse<T, D>>(
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;

/**
Expand All @@ -98,7 +99,7 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
*/
get<T = any, D = any, R = CacheAxiosResponse<T, D>>(
url: string,
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;

/**
Expand All @@ -108,7 +109,7 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
*/
delete<T = any, D = any, R = CacheAxiosResponse<T, D>>(
url: string,
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;

/**
Expand All @@ -118,7 +119,7 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
*/
head<T = any, D = any, R = CacheAxiosResponse<T, D>>(
url: string,
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;

/**
Expand All @@ -128,7 +129,7 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
*/
options<T = any, D = any, R = CacheAxiosResponse<T, D>>(
url: string,
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;

/**
Expand All @@ -139,7 +140,7 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
post<T = any, D = any, R = CacheAxiosResponse<T, D>>(
url: string,
data?: D,
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;

/**
Expand All @@ -150,7 +151,7 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
put<T = any, D = any, R = CacheAxiosResponse<T, D>>(
url: string,
data?: D,
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;

/**
Expand All @@ -161,6 +162,6 @@ export interface AxiosCacheInstance extends CacheInstance, AxiosInstance {
patch<T = any, D = any, R = CacheAxiosResponse<T, D>>(
url: string,
data?: D,
config?: CacheRequestConfig<D>
config?: CacheRequestConfig<T, D>
): Promise<R>;
}
23 changes: 13 additions & 10 deletions src/cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import type { Deferred } from 'fast-defer';
import type { HeadersInterpreter } from '../header/types';
import type { AxiosInterceptor } from '../interceptors/types';
import type { AxiosStorage, CachedResponse } from '../storage/types';
import type { CachePredicate, KeyGenerator } from '../util/types';
import type { CacheUpdater } from '../util/update-cache';
import type { CachePredicate, CacheUpdater, KeyGenerator } from '../util/types';
import type { CacheAxiosResponse, CacheRequestConfig } from './axios';

export type CacheProperties = {
/**
* @template R The type returned by this response
* @template D The type for the request body
*/
export type CacheProperties<R = any, D = any> = {
/**
* The time until the cached value is expired in milliseconds.
*
Expand All @@ -18,7 +21,7 @@ export type CacheProperties = {
*
* @default 1000 * 60 * 5 // 5 Minutes
*/
ttl: number | (<R, D>(response: CacheAxiosResponse<R, D>) => number | Promise<number>);
ttl: number | ((response: CacheAxiosResponse<R, D>) => number | Promise<number>);

/**
* If this interceptor should configure the cache from the request cache header When
Expand All @@ -40,21 +43,21 @@ export type CacheProperties = {
*
* @default {statusCheck: [200, 399]}
*/
cachePredicate: CachePredicate;
cachePredicate: CachePredicate<R, D>;

/**
* Once the request is resolved, this specifies what requests should we change the
* cache. Can be used to update the request or delete other caches.
*
* If the function returns nothing, the entry is deleted
*
* This is independent if the request made was cached or not.
*
* If an provided id represents and loading cache, he will be ignored.
*
* The id used is the same as the id on `CacheRequestConfig['id']`, auto-generated or not.
*
* @default {{}}
*/
update: Record<string, CacheUpdater>;
update: Record<string, CacheUpdater<R, D>>;

/**
* If the request should handle ETag and If-None-Match support. Use a string to force a
Expand Down Expand Up @@ -104,8 +107,8 @@ export interface CacheInstance {
headerInterpreter: HeadersInterpreter;

/** The request interceptor that will be used to handle the cache. */
requestInterceptor: AxiosInterceptor<CacheRequestConfig<any>>;
requestInterceptor: AxiosInterceptor<CacheRequestConfig<unknown, unknown>>;

/** The response interceptor that will be used to handle the cache. */
responseInterceptor: AxiosInterceptor<CacheAxiosResponse<any, any>>;
responseInterceptor: AxiosInterceptor<CacheAxiosResponse<unknown, unknown>>;
}
2 changes: 1 addition & 1 deletion src/cache/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export function setupCache(
axiosCache.storage = storage || buildMemoryStorage();

if (!isStorage(axiosCache.storage)) {
throw new Error('create an storage with buildStorage()');
throw new Error('Use buildStorage()');
}

axiosCache.generateKey = generateKey || defaultKeyGenerator;
Expand Down
14 changes: 7 additions & 7 deletions src/interceptors/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
setRevalidationHeaders
} from './util';

export class CacheRequestInterceptor<D>
implements AxiosInterceptor<CacheRequestConfig<D>>
export class CacheRequestInterceptor
implements AxiosInterceptor<CacheRequestConfig<unknown, unknown>>
{
constructor(readonly axios: AxiosCacheInstance) {}

Expand All @@ -27,8 +27,8 @@ export class CacheRequestInterceptor<D>
};

readonly onFulfilled = async (
config: CacheRequestConfig<D>
): Promise<CacheRequestConfig<D>> => {
config: CacheRequestConfig<unknown>
): Promise<CacheRequestConfig<unknown>> => {
if (config.cache === false) {
return config;
}
Expand Down Expand Up @@ -77,7 +77,7 @@ export class CacheRequestInterceptor<D>
});

if (cache.state === 'stale') {
setRevalidationHeaders(cache, config as ConfigWithCache<D>);
setRevalidationHeaders(cache, config as ConfigWithCache<unknown>);
}

config.validateStatus = createValidateStatus(config.validateStatus);
Expand Down Expand Up @@ -112,8 +112,8 @@ export class CacheRequestInterceptor<D>
* Even though the response interceptor receives this one from here, it has been
* configured to ignore cached responses: true
*/
Promise.resolve<CacheAxiosResponse<any, D>>({
config: config,
Promise.resolve<CacheAxiosResponse<unknown, unknown>>({
config,
data: cachedResponse.data,
headers: cachedResponse.headers,
status: cachedResponse.status,
Expand Down
16 changes: 9 additions & 7 deletions src/interceptors/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import { updateCache } from '../util/update-cache';
import type { AxiosInterceptor } from './types';
import { setupCacheData } from './util';

export class CacheResponseInterceptor<R, D>
implements AxiosInterceptor<CacheAxiosResponse<R, D>>
export class CacheResponseInterceptor
implements AxiosInterceptor<CacheAxiosResponse<unknown, unknown>>
{
constructor(readonly axios: AxiosCacheInstance) {}

Expand All @@ -18,8 +18,8 @@ export class CacheResponseInterceptor<R, D>
};

readonly onFulfilled = async (
axiosResponse: AxiosResponse<R, D>
): Promise<CacheAxiosResponse<R, D>> => {
axiosResponse: AxiosResponse<unknown, unknown>
): Promise<CacheAxiosResponse<unknown, unknown>> => {
const response = this.cachedResponse(axiosResponse);

// Response is already cached
Expand Down Expand Up @@ -101,7 +101,7 @@ export class CacheResponseInterceptor<R, D>

// Update other entries before updating himself
if (cacheConfig?.update) {
updateCache(this.axios.storage, response.data, cacheConfig.update);
updateCache(this.axios.storage, response, cacheConfig.update);
}

const deferred = this.axios.waiting[response.id];
Expand All @@ -126,12 +126,14 @@ export class CacheResponseInterceptor<R, D>
delete this.axios.waiting[key];
};

readonly cachedResponse = (response: AxiosResponse<R, D>): CacheAxiosResponse<R, D> => {
readonly cachedResponse = (
response: AxiosResponse<unknown, unknown>
): CacheAxiosResponse<unknown, unknown> => {
return {
id: this.axios.generateKey(response.config),
// The request interceptor response.cache will return true or undefined. And true only when the response was cached.

cached: (response as CacheAxiosResponse<R, D>).cached || false,
cached: (response as CacheAxiosResponse<unknown, unknown>).cached || false,
...response
};
};
Expand Down
5 changes: 3 additions & 2 deletions src/interceptors/util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Method } from 'axios';
import type { CachedResponse, CacheProperties, StaleStorageValue } from '..';
import type { CacheAxiosResponse, CacheRequestConfig } from '../cache/axios';
import type { CacheProperties } from '../cache/cache';
import type { CachedResponse, StaleStorageValue } from '../storage/types';
import { Header } from '../util/headers';

/**
Expand Down Expand Up @@ -28,7 +29,7 @@ export function isMethodIn(requestMethod: Method, methodList: Method[] = []): bo
return false;
}

export type ConfigWithCache<D> = CacheRequestConfig<D> & {
export type ConfigWithCache<D> = CacheRequestConfig<any, D> & {
cache: Partial<CacheProperties>;
};

Expand Down

0 comments on commit ce2f597

Please sign in to comment.