A component that renders whatever you give it need not know what that is. Typing items as unknown[] throws the caller's knowledge away; a generic keeps it. Declare the type parameter on the function and let each call site infer it.
import type { ReactNode } from 'react';
interface ListProps<T> {
items: readonly T[];
keyOf: (item: T) => string;
children: (item: T) => ReactNode;
}
export function List<T>({ items, keyOf, children }: ListProps<T>) {
return <ul>{items.map(i => <li key={keyOf(i)}>{children(i)}</li>)}</ul>;
}
interface User { id: string; name: string; age: number }
const users: User[] = [{ id: 'u1', name: 'Ada', age: 36 }];
export const good = <List items={users} keyOf={u => u.id}>{u => <b>{u.name}</b>}</List>;
export const bad = <List items={users} keyOf={u => u.id}>{u => <b>{u.nmae}</b>}</List>;src/gen.tsx(14,70): error TS2339: Property 'nmae' does not exist on type 'User'.
T is solved once, from items, and flows into both callbacks: keyOf and the render function receive a User with no annotation anywhere. The same List serves Product[] or readonly string[].
Three refinements matter. Constrain the parameter when the component relies on structure: function List<T extends { id: string }>(...) lets you drop keyOf and write key={item.id}. Tie a second parameter to the first with keyof when a prop names a field — with field: K where K extends keyof T, <Column of={users} field="naem" /> fails with Type '"naem"' is not assignable to type 'keyof User'. And declare generic components with function: in .tsx, const Id = <T>(x: T) => x is parsed as JSX and fails with JSX element 'T' has no corresponding closing tag.