I'm trying to construct a function that accepts a function (react component) as an argument and subsequently returns another (related) function.
I'd like to constrain the incoming function to take arguments of a particular shape (specifically, assert that certain props are present).
I'd like to constrain the outgoing function such that the arguments and return type are related/inferred from the incoming function's "arguments of a particular shape".
That's what I'm trying to do in words. In code, what I've come up with is:
function create<Props, A>(
Component: (props: Props & { callback: (a: A) => void }) => any
): (props: Props) => Promise<A> {
return null as any;
}
function MyComponent(
props: {
id: number, // 'id' and 'name' should be inferred as 'Props'
name: string,
callback: (a: { id: number, status: string }) => void
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// should be inferred as 'A'
}
): void { }
const myComponent = create(MyComponent)
// error on the next line: expected 'callback' to be provided
myComponent({ id: 1, name: 'abc' }).then(res => {
res.id;
// ^? (property) id: number
res.status;
// ^? (property) name: string
})
Here is TS playground link demonstrating the error
Essentially, I'd like to ensure that Component
has a callback
prop, and I'd like for the outgoing (returned) function to take all the same props as Component
, but without callback.
I'm also trying to infer the argument type for callback
(so that it can be used in the returned function's return type Promise<A>
).
From what I can tell Props
is basically inferred to "include" callback
as one of the properties.
If I try to explicitly Omit<Props, 'callback'>
in the returned function I instead get the error:
'Props' could be instantiated with an arbitrary type which could be unrelated to 'Omit<Props, "callback"> & { callback: (a: A) => void; }'
Here is a TS playground link demonstrating this other error
I'm wondering if anyone can help me figure out if what I'm trying to do can be done and/or what I'm missing?
CodePudding user response:
We could just assert that what you're giving to Component
is the correct type:
function create<Props, A>(
Component: (props: Props & { callback: (a: A) => void }) => any
): (props: Omit<Props, "callback">) => Promise<A> {
return (props) => new Promise(
(resolve) => Component({
...props,
callback: (a: A) => { resolve(a) }
} as Props)
);
}
But you'll notice that when we do so, we get an error:
Argument of type 'Props' is not assignable to parameter of type 'Props & { callback: (a: A) => void; }'. Type 'Props' is not assignable to type '{ callback: (a: A) => void; }'.(2345) input.tsx(1, 17): This type parameter might need an
extends { callback: (a: A) => void; }
constraint.
So then that's why I've added the generic constraint to Props
:
function create<Props extends { callback: (a: A) => void }, A>(
and now it works as expected.