typescripttypescript-typingstypescript-genericsreact-table-v7react-table-v6

TS2339: Property 'Cell' does not exist on type from @type/react-table in typescript


I use the @type/react-table to set up the column for my table and I have error on my IDE complain that Cell is not under correct type. I assume it is caused by Cell is optional type from @type/react-table how can I solve this?

//column.tsx
import {Column, Cell} from 'react-table';

export interface ColumnValue {
    [key: string]: any;
}
export type TableColumn = Column<ColumnValue>
export function createColumn(colDef: TableColumn): TableColumn {
  return colDef;
}
export const name = createColumn({
  id: 'name',
  Header: 'Name Column',
  Cell({value}}) {
    return value.hyperlink
  },
});


//column.test.tsx
import {render} from '@testing-library/react';
import {name} from './Name';

describe('Test Name Column', () => {

  it('shows the name', () => {
    const {getByText} = render(
      name.Cell({
      // Error show TS2339: Property 'Cell' does not exist on type 'TableColumn'
        value: {hyperlink: 'asadasd'}}),
      })
    );
    expect(getByText('i am name')).toBeTruthy();
  });
});

Solution

  • The definition of Column is a union of a bunch of different types describing possible column configurations. Only some of them have a Cell property. ColumnGroup does not. Therefore you don't know for sure that a variable of type Column supports the Cell property.

    You can get around this by making your createColumn function generic. It enforces that colDef is assignable to TableColumn but doesn't widen the type.

    export function createColumn<C extends TableColumn>(colDef: C): C {
      return colDef;
    }
    

    Now you get an error further down the chain because Cell expects to be called with the complete CellProps.


    Update:

    The current setup infers the type of the props for a valid Cell in your column configuration as CellProps<ColumnValue, any>. This means that you can just write Cell({value}) { without specifying the props type.

    You cannot make use of the inferred props type for Cell of and also get typescript to infer that your particular Cell only uses the prop value from those (at least not without some advanced Typescript trickery).

    It's easy to declare that the Cell only needs a value prop, but you have to state that explicitly.

    export const name = createColumn({
      id: 'name',
      Header: 'Name Column',
      Cell({value}: {value: ColumnValue}) {
        return value.hyperlink
      },
    });
    

    The render method of React Testing Library expects to be called with a ReactElement. Right now your Cell returns any due to the loose definition of ColumnValue {[key: string]: any;}. But probably value.hyperlink is a string which would be a Typescript error. You should wrap it in a fragment, either in the Cell itself or in the render.

    export const name = createColumn({
      id: 'name',
      Header: 'Name Column',
      Cell({value}: {value: {hyperlink: string}}) {
        return value.hyperlink
      },
    });
    

    The above definition will cause an error in the test so you need to do this:

    const { getByText } = render(
      <>
        {name.Cell({
          value: { hyperlink: "asadasd" }
        })}
      </>
    );