Propagate store enhancer generic type when using composeWithDevTools (#1323)

* Propagate store enhancer generic type when using composeWithDevTools

* Create silent-rats-tickle.md

* Update silent-rats-tickle.md
This commit is contained in:
Nathan Bierema 2023-01-08 19:56:45 -05:00 committed by GitHub
parent c72c021e2d
commit 07456db41e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 61 additions and 14 deletions

View File

@ -0,0 +1,5 @@
---
'@redux-devtools/extension': patch
---
Propagate store enhancer generic type when using composeWithDevTools

View File

@ -591,12 +591,25 @@ const preEnhancer =
} as any; } as any;
}; };
export type InferComposedStoreExt<StoreEnhancers> = StoreEnhancers extends [
infer HeadStoreEnhancer,
...infer RestStoreEnhancers
]
? HeadStoreEnhancer extends StoreEnhancer<infer StoreExt>
? StoreExt & InferComposedStoreExt<RestStoreEnhancers>
: never
: unknown;
const extensionCompose = const extensionCompose =
(config: Config) => (config: Config) =>
(...funcs: StoreEnhancer[]): StoreEnhancer => { <StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
...funcs: StoreEnhancers
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>> => {
// @ts-ignore FIXME
return (...args) => { return (...args) => {
const instanceId = generateId(config.instanceId); const instanceId = generateId(config.instanceId);
return [preEnhancer(instanceId), ...funcs].reduceRight( return [preEnhancer(instanceId), ...funcs].reduceRight(
// @ts-ignore FIXME
(composed, f) => f(composed), (composed, f) => f(composed),
__REDUX_DEVTOOLS_EXTENSION__({ ...config, instanceId })(...args) __REDUX_DEVTOOLS_EXTENSION__({ ...config, instanceId })(...args)
); );
@ -604,8 +617,12 @@ const extensionCompose =
}; };
interface ReduxDevtoolsExtensionCompose { interface ReduxDevtoolsExtensionCompose {
(config: Config): (...funcs: StoreEnhancer[]) => StoreEnhancer; (config: Config): <StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
(...funcs: StoreEnhancer[]): StoreEnhancer; ...funcs: StoreEnhancers
) => StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
<StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
...funcs: StoreEnhancers
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
} }
declare global { declare global {
@ -616,18 +633,24 @@ declare global {
function reduxDevtoolsExtensionCompose( function reduxDevtoolsExtensionCompose(
config: Config config: Config
): (...funcs: StoreEnhancer[]) => StoreEnhancer; ): <StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
...funcs: StoreEnhancers
) => StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
function reduxDevtoolsExtensionCompose<
StoreEnhancers extends readonly StoreEnhancer<unknown>[]
>(
...funcs: StoreEnhancers
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
function reduxDevtoolsExtensionCompose( function reduxDevtoolsExtensionCompose(
...funcs: StoreEnhancer[] ...funcs: [Config] | StoreEnhancer<unknown>[]
): StoreEnhancer; ) {
function reduxDevtoolsExtensionCompose(...funcs: [Config] | StoreEnhancer[]) {
if (funcs.length === 0) { if (funcs.length === 0) {
return __REDUX_DEVTOOLS_EXTENSION__(); return __REDUX_DEVTOOLS_EXTENSION__();
} }
if (funcs.length === 1 && typeof funcs[0] === 'object') { if (funcs.length === 1 && typeof funcs[0] === 'object') {
return extensionCompose(funcs[0]); return extensionCompose(funcs[0]);
} }
return extensionCompose({})(...(funcs as StoreEnhancer[])); return extensionCompose({})(...(funcs as StoreEnhancer<unknown>[]));
} }
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = reduxDevtoolsExtensionCompose; window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ = reduxDevtoolsExtensionCompose;

View File

@ -227,9 +227,22 @@ interface ReduxDevtoolsExtension {
connect: (preConfig: Config) => ConnectResponse; connect: (preConfig: Config) => ConnectResponse;
} }
export type InferComposedStoreExt<StoreEnhancers> = StoreEnhancers extends [
infer HeadStoreEnhancer,
...infer RestStoreEnhancers
]
? HeadStoreEnhancer extends StoreEnhancer<infer StoreExt>
? StoreExt & InferComposedStoreExt<RestStoreEnhancers>
: never
: unknown;
export interface ReduxDevtoolsExtensionCompose { export interface ReduxDevtoolsExtensionCompose {
(config: Config): (...funcs: StoreEnhancer[]) => StoreEnhancer; (config: Config): <StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
(...funcs: StoreEnhancer[]): StoreEnhancer; ...funcs: StoreEnhancers
) => StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
<StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
...funcs: StoreEnhancers
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
} }
declare global { declare global {
@ -241,12 +254,18 @@ declare global {
function extensionComposeStub( function extensionComposeStub(
config: Config config: Config
): (...funcs: StoreEnhancer[]) => StoreEnhancer; ): <StoreEnhancers extends readonly StoreEnhancer<unknown>[]>(
function extensionComposeStub(...funcs: StoreEnhancer[]): StoreEnhancer; ...funcs: StoreEnhancers
function extensionComposeStub(...funcs: [Config] | StoreEnhancer[]) { ) => StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
function extensionComposeStub<
StoreEnhancers extends readonly StoreEnhancer<unknown>[]
>(
...funcs: StoreEnhancers
): StoreEnhancer<InferComposedStoreExt<StoreEnhancers>>;
function extensionComposeStub(...funcs: [Config] | StoreEnhancer<unknown>[]) {
if (funcs.length === 0) return undefined; if (funcs.length === 0) return undefined;
if (typeof funcs[0] === 'object') return compose; if (typeof funcs[0] === 'object') return compose;
return compose(...(funcs as StoreEnhancer[])); return compose(...(funcs as StoreEnhancer<unknown>[]));
} }
export const composeWithDevTools: ReduxDevtoolsExtensionCompose = export const composeWithDevTools: ReduxDevtoolsExtensionCompose =