I have create a generic react component as following:
export interface GenericTableProps<T extends Record<string, unknown>> {
columns: Column<T>[];
data: T[];
}
const GenericTable = <T extends Record<string, unknown>>({
columns,
data,
}: GenericTableProps<T>): ReactElement => {
...
}
This is how I use this component:
const data: StudentsDto[] = useMemo(() => tableData ?? [], [tableData]);
const columns: Column<StudentsDto>[] = useMemo(() => tableColumns, []);
<GenericTable columns={columns} data={data} />
This gives me a typescript error:
Type 'StudentsDto[]' is not assignable to type 'Record<string, unknown>[]'.
Type 'StudentsDto' is not assignable to type 'Record<string, unknown>'.
Index signature for type 'string' is missing in type 'StudentsDto'
StudentsDto looks as following:
export interface StudentsDto {
index?: number;
name?: string;
lastName?: string;
dob?: string;
...
}
I should mention that I can't update StudentsDto interface, since it's generated using openApi.
How can I solve this ?
You can either remove extends Record<string, unknown> or add an index signature to your type const data: StudentsDto & {[key: string]: unknown}[] = ...
The most direct answer is to use Record<string, any> instead of Record<string, unknown>. The general use case of unknown type is when we have to narrow down the types. For example,
function fn(g: unknown) {
if(typeof g === 'string'){
g; //here g would be string
console.log(g.toLowerCase()); // typescript will not yell us about wrong types
}
if(typeof g === 'function'){
g; // function type
console.log(g.name); // can access name property of function
}
}
With Record<string, any> type means that we are assured that it will match with any object and not any type whether they have inferred index signature or not.
The obvious reason why it works with Record<string, any> and not Record<string, unknow> is given here in this issue.
This error is due to typescript's type inference (here's the official doc, and a related answer that can help to understand it)
As @subrato-pattanaik well spotted, unknown should be used when you will narrow types afterwards. And you can use any to accept any type.
For your particular case, if you need to make the interface GenericTableProps to accept different types...
and some of them you do not know at the moment: I'd use an object type, since it is more readable than Record<string, any> (differences here)
export interface GenericTableProps<T extends object> {
//...
and you know all of them: I'd create a new interface extending all the types that GenericTableProps can accept, and use that particular type. This approach is the preferred one because you will have narrower types, and that can help you avoid type errors.
interface GenericTableDataType extends StudentsDto, ProfessorsDto {}
export interface GenericTableProps<T extends GenericTableDataType> {
//...