Reputation: 9007
i have a helper function that take and Object of functions and return a proxy of that object with its functions composed with a first arg injected.
better shown in code:-
// example of object
export const selectors = {
selectA: (state:State) => state.a,
addToA : (state:State, num:number) => state.a + num
}
// ... in another file my helper fn
import {useSelector} from 'react-redux';
// PROBLEM IS HERE...
export const function bindSelector(obj){
return new Proxy(obj,{
get: (main, key, ctx) => {
const fn = main[key].bind(ctx);
// always inject store as first prop of fn
// we get store arg from useSelector higher order (its already properly typed)
return props => useSelector( store=> fn(store, props) )
}
})
}
export default bindSelector(selectors);
so i use Proxy so that i dont have to include useSelector, or pass store param to every selector when i use it
example usage of this would be
import selectors from './a';
// now i can just
const a = selectors.selectA(); // give error param missing
const aAnd2 = selectors.addToA(2); // how to tell typescript that this first arg is in fact the 2nd arg :D !.
selecte require 1 param
because typescript doesnot know that my proxy already supplied first param to function.Upvotes: 0
Views: 516
Reputation: 249686
You can use some mapped and conditional types to map the original object to a new object where each function has one less argument:
import { useSelector } from 'react-redux';
type State = {
a: number
}
// example of object
export const selectors = {
selectA: (state: State) => state.a,
addToA: (state: State, num: number) => state.a + num
}
type CurriedFunctionObject<T> = {
[P in keyof T]: T[P] extends (s: State, ...p: infer P) => infer R ? (...p: P) => R : never
}
export function bindSelector<T extends Record<string, (...a: any) => any>>(obj: T): CurriedFunctionObject<T> {
return new Proxy(obj, {
get: (main, key, ctx) => {
const fn = main[key as keyof T].bind(ctx);
// always inject store as first prop of fn
// we get store arg from useSelector higher order (its already properly typed)
return (args: any[]) => useSelector(store => fn(store, ...args))
}
}) as any as CurriedFunctionObject<T>
}
const cSelectors = bindSelector(selectors);
const a = cSelectors.selectA(); // give error param missing
const aAnd2 = cSelectors.addToA(2)
You can replace State
with any in the conditional type in order to make the type work for any state.
Upvotes: 1