I'm trying to write a React component where its properties change based on the type of a generic property.
interface PropsBase<T> {
value: T;
children: (value: T) => ReactElement;
}
type ArrayType<T extends any[]> = T extends Array<infer U> ? U : never;
interface ArrayProps<T extends any[]> extends Omit<PropsBase<T>, 'children'> {
children: (value: ArrayType<T>, index: number, array: T) => ReactElement;
}
type Props<T> = T extends any[] ? ArrayProps<T> : PropsBase<T>;
const MyComp = <T extends any>({ value, children }: Props<T>) => {
if (Array.isArray(value)) {
return <>{value.map(children)}</>;
}
return <div>{children(value)}</div>;
};
If value is an array, then the child it expects is a function that's applied to each element in the array. Otherwise it expects a function that just takes in the value.
For reasons I have yet to determine, when Props is conditional as it is above, it cannot determine the type of T.
const val = { a: 10 };
const rendered =
<MyComp value={val}>
{value => <span>{value.a}</span>}
</MyComp>;
// error: Object is of type 'unknown'.
This is the case even if both results of the ternary operator are identical.
type Props<T> = T extends any[] ? PropsBase<T> : PropsBase<T>;
// same error
Any other props, including ones only listed in ArrayProps are correctly inferred.
interface ArrayProps<T extends any[]> extends Omit<PropsBase<T>, 'children'> {
children: (value: ArrayType<T>, index: number, array: T) => ReactElement;
arrayOpt: boolean;
}
const array = [ 1, 2, 3 ];
const renderedArray =
<MyComp value={array} arrayOpt>
{value => <span>{value}</span>}
</MyComp>;
If I had to guess, this is a bug in tsc. Meanwhile, is there any way around this?