Can this React component be typed, such that if the Renderer component is defined, itemList can only be an array of parameters for Renderer, and if Renderer is undefined, itemList can only be an array of strings?

export const ListRenderer = ({itemList, Renderer}) => { if (Renderer === undefined) { return <ul> {itemList.map(i => <li>{i}</li>)} </ul> } else { return <ul> {itemList.map(i => <li><Renderer {...i} /></li>)} </ul> } };

The component should accept {itemList: str[]} and {itemList: T[], Renderer: FC<T>} but not {itemList: str[], Renderer: FC<T>}. The first branch should narrow i to have type string, and the second branch should narrow i to have type T. Note that T extends IntrinsicAttributes & {key: Key}

Drew Reese's user avatar

Drew Reese

207k20 gold badges284 silver badges294 bronze badges

Peter Villano's user avatar

I suppose you could define the ListRenderer props to be a union of { itemList: string[]; Renderer?: undefined } or { itemList: string[]; Renderer?: never }, and { itemList: T[]; Renderer: React.FC<T> } so it's one or the other.

Example:

type BaseType = JSX.IntrinsicAttributes & { key: React.Key }; type ListRendererProps<T extends BaseType> = | { itemList: T[]; Renderer: React.FC<T> } | { itemList: string[]; Renderer?: never }; const ListRenderer = <T extends BaseType>({ itemList, Renderer, }: ListRendererProps<T>) => { if (Renderer === undefined) { return ( <ul> {itemList.map((i) => ( <li>{i}</li> ))} </ul> ); } else { return ( <ul> {itemList.map((i) => ( <li> <Renderer {...i} /> </li> ))} </ul> ); } };

Examples:

itemList is string[], Renderer is undefined (happy path):

itemList is string[], Renderer is undefined (happy path)

itemList is string[], Renderer is defined (sad path, the one you want to avoid/omit):

itemList is string[], Renderer is defined (sad path, the one you want to avoid/omit)

itemList is not string[], Renderer is defined (other happy path):

itemList is not string[], Renderer is defined (other happy path)

Drew Reese's user avatar

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.