Home > Blockchain >  Typescript extending generic component type doesn't satisfy the constraint
Typescript extending generic component type doesn't satisfy the constraint

Time:11-16

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

CodePudding user response:

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.

CodePudding user response:

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} />;
  • Related