0

I have a generic component with a generic type, which extends required id property GenericComponent = <T extends { id: number }>. Also I have the interface where id is not required:

interface DataInterface {
    id?: number;
}

In the <App /> component I render it, checking for existing id, but TS expectedly throws the error:

Type 'DataInterface' does not satisfy the constraint '{ id: number; }'.
  Property 'id' is optional in type 'DataInterface' but required in type '{ id: number; }'

I see one way to avoid this: to create a new interface based on my interface and set id as required, like this:

interface DataWithIdInterface extends DataInterface {
    id: number
}

But maybe is there any better way?

The example:

interface DataInterface {
    id?: number;
}

type Props<T> = {
    data: T;
};

const GenericComponent = <T extends { id: number }>({ data }: Props<T>) => (
    <div>{data.id}</div>
);

const App = () => {
    const data = {
        id: undefined
    };

    if (data.id) {
        // error here
        return <GenericComponent<DataInterface> data={data} />;
    }

    return null;
};

Playground

2
  • Why not T extends { id?: number }> instead of T extends { id: number } if it's okay for id to be optional? Commented Nov 15, 2021 at 18:33
  • Just remove explicit generic argument and it will work as expected Commented Nov 15, 2021 at 20:59

2 Answers 2

1

I don't know if it's an option, but you could use Required<T>. With it you can "convert" all optional properties of the given type to required. Maybe something like this?

return <GenericComponent<Required<DataInterface>> data={data} />;
Sign up to request clarification or add additional context in comments.

Comments

1

There's no way to do what you want.

GenericComponent requires a type that is a subtype of { id: number }. { id?: number } (or { id: number | undefined }) is not compatible with that. In short, GenericComponent assumes that id is always going to be a number - you can't provide it with undefined or no value.

{ id?: number } is a supertype of { id: number }, much like how in classic OOP examples an Animal is a supertype of Cat, and you cannot provide an Animal to an interface that specifically requires a Cat.

The only way to do this is with some kind of wrapper, either like you've written in your example or perhaps by writing a higher-order component. Alternatively, just stick with an if statement - no need to write complicated code when simple code does the job.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.