I'm trying to do something kinda connect()
in react-redux bindings.
Here's my HOC that injects props in a component:
export function withAdditionalProps<T, I>(
injectedProps: I,
WrappedComponent: React.ComponentType<T>,
): React.ComponentType<Omit<T, keyof I>> { ... }
It works okay if I declare injected props types (I
generic type), but what If I want to do a HOC without declaring these types (omit injected props keys on-fly). How can I determine keys from the passed props?
I tried, for example, something like this:
export function withAdditionalProps<T>(
injectedProps: { [key: string]: unknown },
WrappedComponent: React.ComponentType<T>,
): React.ComponentType<Omit<T, keyof typeof injectedProps>> { ... }
const InjectedComponent = withAdditionalProps<AppState>(
{
counter: 0
},
(props) => (<div>{props.counter}</div>)
);
But it's not working correct: compiler throws an error when rendering the component. Look at the screenshot (testProp is a "native" prop of a component) Maybe anyone can help me.
In short, your second example isn't possible yet in Typescript.
The issue is that the { [key:string]: unknown }
type always captures all possible strings as keys, rather than narrowing to the concrete ones you use in a particular call, which would make usage of Omit
here possible.
As it is, Omit
used with { [key:string]: unknown }
simply omits all possible keys and therefore all your native props in T
. This may be possible in future via the negated types feature, but for now the only way is via declared type variables as in your first example.
However, those declared type variables in the function definition don't oblige you to also declare them for each call. See the code below - the compiler will infer both T
and I
from the concrete arguments you pass when you call the function. So in practice the only difference here is in the type definitions for your HOC, and honestly it seems like similar effort/readability either way.
function foo<T, I>(t: T, i: I): Omit<T, keyof I> {
return { ...t, ...i }
}
// notice explicitly declaring foo<{ a: number, b: number}, { c: number }>
// is NOT necessary. The correct types are just inferred from the args you pass.
const bar = foo({ a: 1, b: 2 }, { b: 3 })
bar.a // OK
bar.b // ERROR, since b is omitted
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With