Reputation: 449
I have a component List
that has a sub-component Title
, declared using dot-notation.
type Item = {
name: string;
value: number;
};
type ListProps = {
title: string;
data: Item[];
};
export const List: React.FC<ListProps> & {
Title: React.FC<{ title: string }>
} = ({ title, data }) => {
return (
<div>
<List.Title title={title}>
<ul>
{data.map((item) => (
<li>
{item.name}: {item.value}
</li>
))}
</ul>
</div>
);
};
List.Title = ({title}) => <div>{title}</div>
And then i use this component:
<List
title="Stats"
data={[
{ name: "strength", value: 30 },
{ name: "dexterity", value: 50 },
{ name: "intellect", value: 100 },
{ name: "vitality", value: 40 }
]}
/>
At this point everything is fine, but if i need to add generic type parameter to component and its interface it seems like there is no way to do this.
For example i want item name to be of certain type, that i can pass from parent.
<List<"strength" | "dexterity" | "intellect" | "vitality">
title="Stats"
data={[
{ name: "strength", value: 30 },
{ name: "dexterity", value: 50 },
{ name: "intellect", value: 100 },
{ name: "vitality", value: 40 }
]}
/>
I know i can do it like this
type Item<ItemName extends string = string> = {
name: ItemName;
value: number;
};
type ListProps<ItemName extends string = string> = {
title: string;
data: Item<ItemName>[];
};
export const List = <ItemName extends string = string,>({
title,
data
}: ListProps<ItemName>): JSX.Element => {
return (
<div>
<ul>
{data.map((item) => (
<li>
{item.name}: {item.value}
</li>
))}
</ul>
</div>
);
};
// This isn't working, because `List` has no property `Title`
// List.Title = ({ title }) => <div>{title}</div>;
But in this case i don't know how i can specify type for List.Title
. Is there any way to combine this two techniques?
Upvotes: 1
Views: 408
Reputation: 1075915
This isn't working, because List has no property Title
That's not the error TypeScript is raising in the Codesandbox (or in the playground). The error is "Binding element 'title' implicitly has an 'any' type. (7031)" That's because you haven't given any type for the props of List.Title
. The Title
props used to get a type from the type you'd given List
, but since you've removed the explicit type annotation on List
, you need to specify the type of Title
's title
somewhere.
The simple change is to just specify it inline:
List.Title = ({ title }: {title: string; }) => <div>{title}</div>;
// ^^^^^^^^^^^^^^^^^^^
Alternatively, you can do what you did in your first example and give List
an explicit type. The type is:
{
<ItemName extends string = string>({ title, data }: ListProps<ItemName>): JSX.Element;
Title({ title }: {
title: string;
}): JSX.Element;
}
so:
export const List: {
<ItemName extends string = string>({ title, data }: ListProps<ItemName>): JSX.Element;
Title({ title }: {
title: string;
}): JSX.Element;
} = <ItemName extends string = string,>({/*...*/}) => {
// ...
};
List.Title = ({ title }) => <div>{title}</div>;
Upvotes: 1