Material-table: Typescript columns error

Created on 14 May 2020  路  9Comments  路  Source: mbrn/material-table

I am creating a CustomColumn interface definition like this:
export interface CustomColumn<T extends object> extends Column<T> { width?: string; }

and then using a hook to generate columns:
export const useOverviewColumns = (): CustomColumn<OverviewTableData>[] => { ...etc

OverviewTableData defines my data.
I also have some CSS styles that I'm using in the columns:

` const generalCellStyles: React.CSSProperties = {
fontSize: "1.4rem",
color: theme.palette.grey[300],
};

const alignCenter: React.CSSProperties = {
textAlign: "center",
};
`

and I use these like this:
{ title: "Internal Fund Name", field: "internalFundName", cellStyle: { ...generalCellStyles, }, width: "11%", },

However, when I go to use the columns, Typescript throws this error:

Type 'CSSProperties | ((data: OverviewTableData[], rowData: OverviewTableData) => CSSProperties) | undefined' is not assignable to type 'CSSProperties | ((data: object[], rowData: object) => CSSProperties) | undefined'.
Type '(data: OverviewTableData[], rowData: OverviewTableData) => CSSProperties' is not assignable to type 'CSSProperties | ((data: object[], rowData: object) => CSSProperties) | undefined'.
Type '(data: OverviewTableData[], rowData: OverviewTableData) => CSSProperties' is not assignable to type '(data: object[], rowData: object) => CSSProperties'.
Types of parameters 'data' and 'data' are incompatible.
Type 'object[]' is not assignable to type 'OverviewTableData[]'.

I am clearly specifying that my CustomColumn extends object, so I don't see what the issue is. It would be great if someone could help.

ask to stackoverflow has a solution question

Most helpful comment

I think I got it. Define the custom table as generic (see this):

import React, { forwardRef } from 'react';

import AddBox from '@material-ui/icons/AddBox';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import Check from '@material-ui/icons/Check';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import DeleteOutline from '@material-ui/icons/DeleteOutline';
import Edit from '@material-ui/icons/Edit';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Remove from '@material-ui/icons/Remove';
import SaveAlt from '@material-ui/icons/SaveAlt';
import Search from '@material-ui/icons/Search';
import ViewColumn from '@material-ui/icons/ViewColumn';

import MaterialTable, { MaterialTableProps, Options, Icons } from 'material-table';

// Set the table icons directly from Material-UI, see https://github.com/mbrn/material-table/issues/1004#issuecomment-525274793
const tableIcons: Icons = {
  // eslint-disable-next-line react/display-name
  Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  SortArrow: forwardRef((props, ref) => <ArrowDownward {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />),
};
export interface CustomTableProps<T extends object> extends MaterialTableProps<T> {
  rowsPerPage?: number;
}

/**
 * Render a custom Material UI table, including all necessary icons.
 *
 * By default, no filtering, search, or toolbar is enabled.
 * All props are passed to the Material table itself.
 *
 * See https://material-table.com/#/ for more
 */
const CustomTable: <T extends object>(props: CustomTableProps<T>) => JSX.Element = <T extends object>({
  rowsPerPage = 10,
  ...props
}: CustomTableProps<T>) => {
  const options: Options = Object.assign(props.options || {}, {
    filtering: false,
    search: false,
    toolbar: false,
    pageSize: rowsPerPage,
    pageSizeOptions: [5, 25, 50, 100],
  });

  return <MaterialTable icons={tableIcons} options={options} {...props} />;
};

export default CustomTable;

Then instantiate it like this, where you pass the actual column type and any other props you may want to forward:

<CustomTable<SomeType> columns={columns} data={data} {...props} />

All 9 comments

To anyone else having trouble with this, React.memo was the cause, it was messing up a generic declaration

I'm getting a similar error. I want to define a custom table that takes in the same props as the MaterialTable:

export interface CustomTableProps<T extends object> extends MaterialTableProps<T> {}

And I want it to take all kinds of row data.

So I instantiate it like this:

export interface RecentMeasurementsTableProps extends Omit<CustomTableProps<StatisticReport>, 'data' | 'columns'> {}

And define my columns as such:

const columns: Column<StatisticReport>[] = [];

And when I use <CustomTable columns={columns}>, I get:

Type 'Column<StatisticReport>[]' is not assignable to type 'Column<object>[]'.
  Type 'Column<StatisticReport>' is not assignable to type 'Column<object>'.
    Types of property 'cellStyle' are incompatible.
      Type 'CSSProperties | ((data: StatisticReport[], rowData: StatisticReport) => CSSProperties) | undefined' is not assignable to type 'CSSProperties | ((data: object[], rowData: object) => CSSProperties) | undefined'.
        Type '(data: StatisticReport[], rowData: StatisticReport) => CSSProperties' is not assignable to type 'CSSProperties | ((data: object[], rowData: object) => CSSProperties) | undefined'.
          Type '(data: StatisticReport[], rowData: StatisticReport) => CSSProperties' is not assignable to type '(data: object[], rowData: object) => CSSProperties'.
            Types of parameters 'data' and 'data' are incompatible.
              Type 'object[]' is not assignable to type 'StatisticReport[]'.
                Type 'object' is not assignable to type 'StatisticReport'.ts(2322)
index.d.ts(7, 3): The expected type comes from property 'columns' which is declared here on type 'IntrinsicAttributes & CustomTableProps<object> & { children?: ReactNode; }'

Any idea?

@slhck How are you actually using your custom component? What worked for me was I made a component called CustomTable and then called
<CustomTable<MyDataObjectInterface> {...props} />

Post your code if you want me to have a look because I solved this exact problem on Friday

I have a custom table:

export interface CustomTableProps<T extends object> extends MaterialTableProps<T> {
}

const CustomTable: React.FC<CustomTableProps<object>> = (
  props: CustomTableProps<object> = {
    data: [],
    columns: [],
  },
): JSX.Element => {
  return (
    <MaterialTable
      options={{
        filtering: false,
        search: false,
        toolbar: false,
      }}
      {...props}
    />
  );
};

export default CustomTable;

And when I want to use it:

export interface RecentMeasurementsTableProps extends Omit<CustomTableProps<StatisticReport>, 'data' | 'columns'> {
  extended?: boolean;
}

const RecentMeasurementsTable: React.FC<RecentMeasurementsTableProps> = (
  props: RecentMeasurementsTableProps = { extended: false },
): JSX.Element => {
  const columns: Column<StatisticReport>[] = [
    // ...
  ];

  const data = [
    // ...
  ]

  if (props.extended) {
    // other stuff
  }

  return <CustomTable columns={columns} data={data} />;
};

I notice that I did not actually use generics for creating <CustomTable> ...

I think I got it. Define the custom table as generic (see this):

import React, { forwardRef } from 'react';

import AddBox from '@material-ui/icons/AddBox';
import ArrowDownward from '@material-ui/icons/ArrowDownward';
import Check from '@material-ui/icons/Check';
import ChevronLeft from '@material-ui/icons/ChevronLeft';
import ChevronRight from '@material-ui/icons/ChevronRight';
import Clear from '@material-ui/icons/Clear';
import DeleteOutline from '@material-ui/icons/DeleteOutline';
import Edit from '@material-ui/icons/Edit';
import FilterList from '@material-ui/icons/FilterList';
import FirstPage from '@material-ui/icons/FirstPage';
import LastPage from '@material-ui/icons/LastPage';
import Remove from '@material-ui/icons/Remove';
import SaveAlt from '@material-ui/icons/SaveAlt';
import Search from '@material-ui/icons/Search';
import ViewColumn from '@material-ui/icons/ViewColumn';

import MaterialTable, { MaterialTableProps, Options, Icons } from 'material-table';

// Set the table icons directly from Material-UI, see https://github.com/mbrn/material-table/issues/1004#issuecomment-525274793
const tableIcons: Icons = {
  // eslint-disable-next-line react/display-name
  Add: forwardRef((props, ref) => <AddBox {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Check: forwardRef((props, ref) => <Check {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Clear: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Delete: forwardRef((props, ref) => <DeleteOutline {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  DetailPanel: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Edit: forwardRef((props, ref) => <Edit {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Export: forwardRef((props, ref) => <SaveAlt {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Filter: forwardRef((props, ref) => <FilterList {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  FirstPage: forwardRef((props, ref) => <FirstPage {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  LastPage: forwardRef((props, ref) => <LastPage {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  NextPage: forwardRef((props, ref) => <ChevronRight {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  PreviousPage: forwardRef((props, ref) => <ChevronLeft {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  ResetSearch: forwardRef((props, ref) => <Clear {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  Search: forwardRef((props, ref) => <Search {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  SortArrow: forwardRef((props, ref) => <ArrowDownward {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  ThirdStateCheck: forwardRef((props, ref) => <Remove {...props} ref={ref} />),
  // eslint-disable-next-line react/display-name
  ViewColumn: forwardRef((props, ref) => <ViewColumn {...props} ref={ref} />),
};
export interface CustomTableProps<T extends object> extends MaterialTableProps<T> {
  rowsPerPage?: number;
}

/**
 * Render a custom Material UI table, including all necessary icons.
 *
 * By default, no filtering, search, or toolbar is enabled.
 * All props are passed to the Material table itself.
 *
 * See https://material-table.com/#/ for more
 */
const CustomTable: <T extends object>(props: CustomTableProps<T>) => JSX.Element = <T extends object>({
  rowsPerPage = 10,
  ...props
}: CustomTableProps<T>) => {
  const options: Options = Object.assign(props.options || {}, {
    filtering: false,
    search: false,
    toolbar: false,
    pageSize: rowsPerPage,
    pageSizeOptions: [5, 25, 50, 100],
  });

  return <MaterialTable icons={tableIcons} options={options} {...props} />;
};

export default CustomTable;

Then instantiate it like this, where you pass the actual column type and any other props you may want to forward:

<CustomTable<SomeType> columns={columns} data={data} {...props} />

That's it, glad you solved.

Perhaps you want to close this issue if you solved it?

Closing this issue out. Please reopen if needed.

Was this page helpful?
0 / 5 - 0 ratings