Home > Net >  Inferred type parameter in generic typescript function is too wide
Inferred type parameter in generic typescript function is too wide

Time:11-11

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.

Playground

  • Related