TanStack / table

🤖 Headless UI for building powerful tables & datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table
https://tanstack.com/table
MIT License
25.33k stars 3.1k forks source link

columnHelper.accessor Type issue #4241

Closed opti21 closed 1 year ago

opti21 commented 2 years ago

Describe the bug

When defining the columns for the table, columnHelper.accessor seems to cause a type issue. Although, table seems to render fine.

Type error: Type 'ColumnDef<Room, string>' is not assignable to type 'ColumnDef<Room, unknown>'.

StackBlitz Type Error:

Type instantiation is excessively deep and possibly infinite.(2589)
(property) accessor: <"id", string>(accessor: "id", column: Omit<ColumnDef<Room, string>, "accessorKey">) => ColumnDef<Room, string>

Your minimal, reproducible example

https://stackblitz.com/edit/github-sq9c9a?file=pages%2Findex.tsx

Steps to reproduce

Line 22 on index.tsx

Expected behavior

No type error

How often does this bug happen?

Every time

Screenshots or Videos

No response

Platform

Windows 11 Pro Chrome: Version 103.0.5060.134 (Official Build) (64-bit)

react-table version

8.5.2

TypeScript version

4.7.4

Additional context

No response

Terms & Code of Conduct

dustinwdev commented 2 years ago

We just installed the latest version with a fresh Next.js app and I am seeing the exact same issue with columnHelper types.

tannerlinsley commented 2 years ago

Working on a fix right now

Tanner Linsley On Jul 29, 2022, 3:55 PM -0600, Dustin Wilson @.***>, wrote:

We just installed the latest version with a fresh Next.js app and I am seeing the exact same issue with columnHelper types. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.Message ID: @.***>

ebramanti commented 2 years ago

Is this related? https://github.com/TanStack/table/discussions/4249

tannerlinsley commented 2 years ago

We just shipped new types for the columnHelper along with better underlying column def types. Will you try them out and report back?

ebramanti commented 2 years ago

My compiler is still hanging with this type @tannerlinsley

opti21 commented 2 years ago

Both vscode and stackblitz still show the error for me. Updated stackblitz https://stackblitz.com/edit/github-sq9c9a?file=package.json,pages%2Findex.tsx

tannerlinsley commented 2 years ago

I'm definitely seeing the error in stackblitz, but can't replicate locally in the examples. Is stackblitz on the latest version of TS?

opti21 commented 2 years ago

both local and the stackblitz is on 4.7.4 and it still shows the error.

sjbuysse commented 2 years ago

Same here, haven't been able to use the columnHelper since it's creation not too long ago.

Obiwarn commented 2 years ago

FYI: updated to v8.5.6 labeled:

Fix type instantiation is excessively deep (https://github.com/TanStack/table/pull/4262) (https://github.com/TanStack/table/commit/a9c4ded243a105a54059f76754963b5e084125ff) by @Himself65

still shows the error.

victorkvarghese commented 2 years ago

Same here

i tried 8.5.5 then 8.5.6. Only change i made was to convert from array of columDef to ColumHelper format

Both are throwing multiple errors.

TS2589: Type instantiation is excessively deep and possibly infinite. and also type errors too

  TS2345: Argument of type '{ id: string; size: number; header: ({ header }: { header: any; }) => Element; cell: ({ row }: { row: any; }) => Element; }' is not assignable to parameter of type 'Column<T, unknown>'.
  Type '{ id: string; size: number; header: ({ header }: { header: any; }) => Element; cell: ({ row }: { row: any; }) => Element; }' is missing the following properties from type 'CoreColumn<T, unknown>': depth, columnDef, columns, getFlatColumns, getLeafColumns

FYI: Array of columnDefs still works

himself65 commented 2 years ago
TS2322: Type 'ColumnDef<Room, string>' is not assignable to type 'ColumnDef<Room, unknown>'.   Type '{ footer?: ColumnDefTemplate<HeaderContext<Room, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type 'ColumnDef<Room, unknown>'.     Type '{ footer?: ColumnDefTemplate<HeaderContext<Room, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<Room, unknown>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, unknown>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 7 more ... & { ...; }'.       Type '{ footer?: ColumnDefTemplate<HeaderContext<Room, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<Room, unknown>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, unknown>> | undefined; meta?: ColumnMeta<...> | undefined; }'.         Types of property 'footer' are incompatible.           Type 'ColumnDefTemplate<HeaderContext<Room, string>> | undefined' is not assignable to type 'ColumnDefTemplate<HeaderContext<Room, unknown>> | undefined'.             Type '(props: HeaderContext<Room, string>) => any' is not assignable to type 'ColumnDefTemplate<HeaderContext<Room, unknown>> | undefined'.               Type '(props: HeaderContext<Room, string>) => any' is not assignable to type '(props: HeaderContext<Room, unknown>) => any'.                 Types of parameters 'props' and 'props' are incompatible.                   Type 'HeaderContext<Room, unknown>' is not assignable to type 'HeaderContext<Room, string>'.                     Types of property 'header' are incompatible.                       Type 'Header<Room, unknown>' is not assignable to type 'Header<Room, string>'.                         Type 'Header<Room, unknown>' is not assignable to type 'CoreHeader<Room, string>'.                           Types of property 'column' are incompatible.                             Type 'Column<Room, unknown>' is not assignable to type 'Column<Room, string>'.                               Type 'Column<Room, unknown>' is not assignable to type 'CoreColumn<Room, string>'.                                 Types of property 'accessorFn' are incompatible.                                   Type 'AccessorFn<Room, unknown> | undefined' is not assignable to type 'AccessorFn<Room, string> | undefined'.                                     Type 'AccessorFn<Room, unknown>' is not assignable to type 'AccessorFn<Room, string>'.                                       Type 'unknown' is not assignable to type 'string'.
himself65 commented 2 years ago

I think one solution is change TValue from ColumnDef<TData extends RowData, TValue = unknown> to TValue = any

himself65 commented 2 years ago

workaround for now:

const columns = ColumnDef<Room, any> = [
  //...
]
tannerlinsley commented 2 years ago

Just pushed a patch that hopefully fixes this TS issue. Please upgrade and confirm/deny please :)

himself65 commented 2 years ago

Just pushed a patch that hopefully fixes this TS issue. Please upgrade and confirm/deny please :)

Not work, because columnHelper.accessor returns Column<Room, string> but the type of array is Column<Room, unknown>, and this error is not related to the recursive type. Just basically unknown cannot be the string

opti21 commented 2 years ago

Oddly the stackblitz is fixed but locally it's not.

function(info: CellContext<Room, string>): string
Type 'ColumnDef<Room, string>' is not assignable to type 'ColumnDef<Room, unknown>'.
  Type '{ footer?: ColumnDefTemplate<HeaderContext<Room, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type 'ColumnDef<Room, unknown>'.
    Type '{ footer?: ColumnDefTemplate<HeaderContext<Room, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<Room, unknown>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, unknown>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 7 more ... & { ...; }'.
      Type '{ footer?: ColumnDefTemplate<HeaderContext<Room, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<Room, unknown>> | undefined; cell?: ColumnDefTemplate<CellContext<Room, unknown>> | undefined; meta?: ColumnMeta<...> | undefined; }'.
        Types of property 'footer' are incompatible.
          Type 'ColumnDefTemplate<HeaderContext<Room, string>> | undefined' is not assignable to type 'ColumnDefTemplate<HeaderContext<Room, unknown>> | undefined'.
            Type '(props: HeaderContext<Room, string>) => any' is not assignable to type 'ColumnDefTemplate<HeaderContext<Room, unknown>> | undefined'.
              Type '(props: HeaderContext<Room, string>) => any' is not assignable to type '(props: HeaderContext<Room, unknown>) => any'.
                Types of parameters 'props' and 'props' are incompatible.
                  Type 'HeaderContext<Room, unknown>' is not assignable to type 'HeaderContext<Room, string>'.
                    Types of property 'header' are incompatible.
                      Type 'Header<Room, unknown>' is not assignable to type 'Header<Room, string>'.
                        Type 'Header<Room, unknown>' is not assignable to type 'CoreHeader<Room, string>'.
                          Types of property 'column' are incompatible.
                            Type 'Column<Room, unknown>' is not assignable to type 'Column<Room, string>'.
                              Type 'Column<Room, unknown>' is not assignable to type 'CoreColumn<Room, string>'.
                                Types of property 'accessorFn' are incompatible.
                                  Type 'AccessorFn<Room, unknown> | undefined' is not assignable to type 'AccessorFn<Room, string> | undefined'.
                                    Type 'AccessorFn<Room, unknown>' is not assignable to type 'AccessorFn<Room, string>'.
                                      Type 'unknown' is not assignable to type 'string'.ts(2322)
tannerlinsley commented 2 years ago

When cloning this locally, I don't see the issue any more. Can you provide any more info? Screenshots? Video?

opti21 commented 2 years ago

image

ebramanti commented 2 years ago

I am getting this error on mine:

error TS2589: Type instantiation is excessively deep and possibly infinite.

 52   {
      ~
 53     accessorKey: 'price',
    ~~~~~~~~~~~~~~~~~~~~~~~~~
... 
 58     },
    ~~~~~~
 59   },
    ~~~

This is for a ColumnDef type

tannerlinsley commented 2 years ago

We just removed the deep key functionality for the generic ColumnDef type. This should help.

As for the cannot assign to "unknown" issue, this is actually intended after debugging. If you are trying to assign a bunch of columns with known values to an unknown type then it's expected to fail. The accurate type to capture a bunch of different column types would be:

const columnDefs: ColumnDef<RowType, any>[] = [...]
opti21 commented 2 years ago

Ah ok, adding the any fixed it for the local, it may be how prisma generates the type for Room. But yes all seems good now. Thanks for the hard work @tannerlinsley

rstrand commented 2 years ago

The TypeScript documentation highly discourages the use of any. Are there any good alternatives to the ColumnDef<RowType, any>[] solution? https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#any

himself65 commented 2 years ago

The TypeScript documentation highly discourages the use of any. Are there any good alternatives to the ColumnDef<RowType, any>[] solution?

https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#any

string

rstrand commented 2 years ago

I've also noticed that using any seems to bread the typings inside the accessor. It seems to force the value returned from info.getValue() to be of type any.

rstrand commented 2 years ago

The TypeScript documentation highly discourages the use of any. Are there any good alternatives to the ColumnDef<RowType, any>[] solution? https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#any

string

Using string only works if all my columns are of type string. If I try to use something like ColumnDef<RowType, string | number>[] I just run into the same issue as before.

himself65 commented 2 years ago

The TypeScript documentation highly discourages the use of any. Are there any good alternatives to the ColumnDef<RowType, any>[] solution?

https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#any

string

Using string only works if all my columns are of type string. If I try to use something like ColumnDef<RowType, string | number>[] I just run into the same issue as before.

i was meant to use DeepKeys, but it seems have recurision issue now

tannerlinsley commented 2 years ago

any is not discouraged. This is the perfect case to use it because you are describing an array of column defs with varying generic types. If you want to avoid it, then don’t pre type your column def array. Simply define an array and fill it with column defs created with the helper. This will result in an array with and inferred type of its contents.

djstein commented 2 years ago

this hit me today when running tsc --noEmit to do type validation.

on main branch no issues. I can switch to a feature branch with NO frontend application changes and it is going crazy. I wonder if this has something to do with PNPM + Vercel's TurboRepo.

Dependencies: TOP LEVEL APP:

dependencies:
@babel/core 7.18.5

devDependencies:
eslint-config-custom link:packages/eslint-config-custom
prettier 2.7.1
turbo 1.4.2

CHILD:

dependencies:
@babel/core 7.18.5               next 12.1.6                      react-dom 17.0.2                 styled-components 5.3.5
@heroicons/react 1.0.6           next-themes 0.2.0                react-query 3.39.1               ui link:../../packages/ui
**@tanstack/react-table 8.5.11**     react 17.0.2                     sdk link:../../packages/sdk      utils link:../../packages/utils

devDependencies:
@types/node 17.0.45
@types/react 17.0.37
@types/styled-components 5.1.25
babel-plugin-styled-components 2.0.7
eslint 7.32.0
eslint-config-custom link:../../packages/eslint-config-custom
next-transpile-modules 9.0.0
tsconfig link:../../packages/tsconfig
**typescript 4.7.4**
znat commented 2 years ago

I am still having the issue on 8.5.13:

type KegLocation = {
  zone: string;
  count: number;
  empty: number;
};

const columnHelper = createColumnHelper<KegLocation>();

const columns: ColumnDef<KegLocation, string | number | null>[] = [
  columnHelper.accessor('zone', {
    cell: (info) => info.renderValue(),
    header: () => <span>Zone</span>,
  }),
  columnHelper.accessor('count', {
    header: () => 'Total',
    cell: (info) => info.renderValue(),
  }),
  columnHelper.accessor('empty', {
    header: () => 'Empty',
    cell: (info) => info.renderValue(),
  }),
];

function AllKegsByLocation() {
  const { kegLocations, isLoading, error } = useKegsByLocation();

  const [sorting, setSorting] = React.useState<SortingState>([]);
  const table = useReactTable({
    data: kegLocations,
    columns, // error here
    state: {
      sorting,
    },
    onSortingChange: setSorting,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    debugTable: true,
  });
...
}

Where the error is:

Type 'ColumnDef<KegLocation, string | number | null>[]' is not assignable to type 'ColumnDef<unknown, any>[]'.
  Type 'ColumnDef<KegLocation, string | number | null>' is not assignable to type 'ColumnDef<unknown, any>'.
    Type '{ footer?: ColumnDefTemplate<HeaderContext<KegLocation, string | number | null>> | undefined; cell?: ColumnDefTemplate<CellContext<KegLocation, string | ... 1 more ... | null>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type 'ColumnDef<unknown, any>'.
      Type '{ footer?: ColumnDefTemplate<HeaderContext<KegLocation, string | number | null>> | undefined; cell?: ColumnDefTemplate<CellContext<KegLocation, string | ... 1 more ... | null>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<unknown, any>> | undefined; cell?: ColumnDefTemplate<CellContext<unknown, any>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 7 more ... & { ...; }'.
        Type '{ footer?: ColumnDefTemplate<HeaderContext<KegLocation, string | number | null>> | undefined; cell?: ColumnDefTemplate<CellContext<KegLocation, string | ... 1 more ... | null>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<unknown, any>> | undefined; cell?: ColumnDefTemplate<CellContext<unknown, any>> | undefined; meta?: ColumnMeta<...> | undefined; }'.
          Types of property 'footer' are incompatible.
            Type 'ColumnDefTemplate<HeaderContext<KegLocation, string | number | null>> | undefined' is not assignable to type 'ColumnDefTemplate<HeaderContext<unknown, any>> | undefined'.
              Type '(props: HeaderContext<KegLocation, string | number | null>) => any' is not assignable to type 'ColumnDefTemplate<HeaderContext<unknown, any>> | undefined'.
                Type '(props: HeaderContext<KegLocation, string | number | null>) => any' is not assignable to type '(props: HeaderContext<unknown, any>) => any'.
                  Types of parameters 'props' and 'props' are incompatible.
                    Type 'HeaderContext<unknown, any>' is not assignable to type 'HeaderContext<KegLocation, string | number | null>'.
                      Types of property 'table' are incompatible.
                        Type 'Table<unknown>' is not assignable to type 'Table<KegLocation>'.
                          Type 'Table<unknown>' is not assignable to type 'CoreInstance<KegLocation>'.
                            Types of property 'options' are incompatible.
                              Type 'RequiredKeys<TableOptionsResolved<unknown>, "state">' is not assignable to type 'RequiredKeys<TableOptionsResolved<KegLocation>, "state">'.
                                Type 'RequiredKeys<TableOptionsResolved<unknown>, "state">' is not assignable to type 'Omit<TableOptionsResolved<KegLocation>, "state">'.
                                  Types of property 'kegs' are incompatible.
                                    Type 'unknown[]' is not assignable to type 'KegLocation[]'.
                                      Type 'unknown' is not assignable to type 'KegLocation'.ts(2322)
index.d.ts(748, 5): The expected type comes from property 'columns' which is declared here on type 'TableOptions<unknown>'
JacobWeisenburger commented 2 years ago

I'm getting this same issue too.

package.json

"dependencies": {
    "@tanstack/react-table": "^8.5.13",
},

user-list.tsx

columnHelper.accessor( 'company.name', { header: 'Company' } ),
// Type 'ColumnDef<User, string | undefined>' is not assignable to type 'ColumnDef<User, unknown>'.

full error

Type 'ColumnDef<User, string | undefined>' is not assignable to type 'ColumnDef<User, unknown>'.
  Type '{ footer?: ColumnDefTemplate<HeaderContext<User, string | undefined>> | undefined; cell?: ColumnDefTemplate<CellContext<User, string | undefined>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type 'ColumnDef<User, unknown>'.
    Type '{ footer?: ColumnDefTemplate<HeaderContext<User, string | undefined>> | undefined; cell?: ColumnDefTemplate<CellContext<User, string | undefined>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<User, unknown>> | undefined; cell?: ColumnDefTemplate<CellContext<User, unknown>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 7 more ... & { ...; }'.
      Type '{ footer?: ColumnDefTemplate<HeaderContext<User, string | undefined>> | undefined; cell?: ColumnDefTemplate<CellContext<User, string | undefined>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<User, unknown>> | undefined; cell?: ColumnDefTemplate<CellContext<User, unknown>> | undefined; meta?: ColumnMeta<...> | undefined; }'.
        Types of property 'footer' are incompatible.
          Type 'ColumnDefTemplate<HeaderContext<User, string | undefined>> | undefined' is not assignable to type 'ColumnDefTemplate<HeaderContext<User, unknown>> | undefined'.
            Type '(props: HeaderContext<User, string | undefined>) => any' is not assignable to type 'ColumnDefTemplate<HeaderContext<User, unknown>> | undefined'.
              Type '(props: HeaderContext<User, string | undefined>) => any' is not assignable to type '(props: HeaderContext<User, unknown>) => any'.
                Types of parameters 'props' and 'props' are incompatible.
                  Type 'HeaderContext<User, unknown>' is not assignable to type 'HeaderContext<User, string | undefined>'.
                    Types of property 'header' are incompatible.
                      Type 'Header<User, unknown>' is not assignable to type 'Header<User, string | undefined>'.
                        Type 'Header<User, unknown>' is not assignable to type 'CoreHeader<User, string | undefined>'.
                          Types of property 'column' are incompatible.
                            Type 'Column<User, unknown>' is not assignable to type 'Column<User, string | undefined>'.
                              Type 'Column<User, unknown>' is not assignable to type 'CoreColumn<User, string | undefined>'.
                                Types of property 'accessorFn' are incompatible.
                                  Type 'AccessorFn<User, unknown> | undefined' is not assignable to type 'AccessorFn<User, string | undefined> | undefined'.
                                    Type 'AccessorFn<User, unknown>' is not assignable to type 'AccessorFn<User, string | undefined>'.
                                      Type 'unknown' is not assignable to type 'string | undefined'.ts(2322)
firxworx commented 2 years ago

I get same issue if a field value is a TypeScript enum (v8.5.13) -- Perhaps re-open this issue?

FYI the issue can manifest even if one doesn't "pre type [the] column def array" as @tannerlinsley suggests above (https://github.com/TanStack/table/issues/4241#issuecomment-1208478767).

In my case it presented an obstacle for a particular generic data table implementation. I disagree w/ use of any as it can impact types elsewhere (an example is noted by @rstrand above) and overall imho this works against the entire point of TS. There is a risk of imposing downstream compromises in codebases and overall limiting the quality of reusable generic components that leverage react-table.

I'm working around with any for now plus // eslint-disable-next-line @typescript-eslint/no-explicit-any. It would be great to kill that some day :)

jonahallibone commented 2 years ago

Truly I cannot tell if this is a bug or not. It seems like it should not be a problem.

const columnHelper = createColumnHelper<Foundation>();

const columns = [
  columnHelper.accessor("id", {
    cell: (info) => info.getValue(),
    header: "ID",
  }),
  columnHelper.accessor("displayName", {
    cell: (info) => info.getValue(),
    header: "Display Name",
  }),
  columnHelper.accessor("ein", {
    cell: (info) => info.getValue(),
    header: "EIN",
  }),
  columnHelper.accessor("subdomain", {
    cell: (info) => <Code textTransform="lowercase">{info.getValue()}</Code>,
    header: "Subdomain",
  }),
  columnHelper.accessor("created", {
    cell: (info) => info.getValue(),
    header: "Created on",
  }),
  {
    id: "actions",
    header: () => "Actions",
    cell: ({ row }: { row: Row<Foundation> }) => (
      <Center>
        <Button
          as={Link}
          to={`/foundation/${row.getValue("id")}`}
          colorScheme="green"
          _hover={{
            color: "white",
          }}
        >
          Visit
        </Button>
      </Center>
    ),
  },
];

const FoundationTable = ({ data }: { data: Foundation[] }) => (
  <GeneralTable data={data} columns={columns} />
);
Type 'ColumnDef<Foundation, string>[]' is not assignable to type 'ColumnDef<Foundation, unknown>[]'.
  Type 'ColumnDef<Foundation, string>' is not assignable to type 'ColumnDef<Foundation, unknown>'.
    Type '{ footer?: ColumnDefTemplate<HeaderContext<Foundation, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Foundation, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type 'ColumnDef<Foundation, unknown>'.
      Type '{ footer?: ColumnDefTemplate<HeaderContext<Foundation, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Foundation, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<Foundation, unknown>> | undefined; cell?: ColumnDefTemplate<CellContext<Foundation, unknown>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 7 more ... & { ...; }'.
        Type '{ footer?: ColumnDefTemplate<HeaderContext<Foundation, string>> | undefined; cell?: ColumnDefTemplate<CellContext<Foundation, string>> | undefined; meta?: ColumnMeta<...> | undefined; } & ... 6 more ... & StringHeaderIdentifier' is not assignable to type '{ footer?: ColumnDefTemplate<HeaderContext<Foundation, unknown>> | undefined; cell?: ColumnDefTemplate<CellContext<Foundation, unknown>> | undefined; meta?: ColumnMeta<...> | undefined; }'.
          Types of property 'footer' are incompatible.
            Type 'ColumnDefTemplate<HeaderContext<Foundation, string>> | undefined' is not assignable to type 'ColumnDefTemplate<HeaderContext<Foundation, unknown>> | undefined'.
              Type '(props: HeaderContext<Foundation, string>) => any' is not assignable to type 'ColumnDefTemplate<HeaderContext<Foundation, unknown>> | undefined'.
                Type '(props: HeaderContext<Foundation, string>) => any' is not assignable to type '(props: HeaderContext<Foundation, unknown>) => any'.
                  Types of parameters 'props' and 'props' are incompatible.
                    Type 'HeaderContext<Foundation, unknown>' is not assignable to type 'HeaderContext<Foundation, string>'.
                      Types of property 'header' are incompatible.
                        Type 'Header<Foundation, unknown>' is not assignable to type 'Header<Foundation, string>'.
                          Type 'Header<Foundation, unknown>' is not assignable to type 'CoreHeader<Foundation, string>'.
                            Types of property 'column' are incompatible.
                              Type 'Column<Foundation, unknown>' is not assignable to type 'Column<Foundation, string>'.
                                Type 'Column<Foundation, unknown>' is not assignable to type 'CoreColumn<Foundation, string>'.
                                  Types of property 'accessorFn' are incompatible.
                                    Type 'AccessorFn<Foundation, unknown> | undefined' is not assignable to type 'AccessorFn<Foundation, string> | undefined'.
                                      Type 'AccessorFn<Foundation, unknown>' is not assignable to type 'AccessorFn<Foundation, string>'.
                                        Type 'unknown' is not assignable to type 'string'.ts(2322)
general-table.tsx(28, 3): The expected type comes from property 'columns' which is declared here on type 'IntrinsicAttributes & { data: Foundation[]; columns: ColumnDef<Foundation, unknown>[]; }'

general-table.tsx

const GeneralTable = <DataType,>({
  data,
  columns,
}: {
  data: DataType[];
  columns: ColumnDef<DataType>[];
}) => {
  const [sorting, setSorting] = useState<SortingState>([]);
  const table = useReactTable({
    columns,
    data,
    getCoreRowModel: getCoreRowModel(),
    onSortingChange: setSorting,
    getSortedRowModel: getSortedRowModel(),
    state: {
      sorting,
    },
  });

  return (
    <Box maxW="100%" overflowX="auto">
      <TableContainer>
        <Table variant="simple">
          <Thead>
            {table.getHeaderGroups().map((headerGroup) => (
              <Tr key={headerGroup.id}>
                {headerGroup.headers.map((header) => (
                  <Th
                    key={header.id}
                    onClick={header.column.getToggleSortingHandler()}
                  >
                    {flexRender(
                      header.column.columnDef.header,
                      header.getContext()
                    )}

                    <chakra.span pl="4">
                      {header.column.getIsSorted() === "desc" && (
                        <Icon
                          as={RiArrowDownLine}
                          aria-label="sorted descending"
                        />
                      )}
                      {header.column.getIsSorted() === "asc" && (
                        <Icon
                          as={RiArrowUpLine}
                          aria-label="sorted ascending"
                        />
                      )}
                    </chakra.span>
                  </Th>
                ))}
              </Tr>
            ))}
          </Thead>
          <Tbody>
            {table.getRowModel().rows.map((row) => (
              <Tr key={row.id}>
                {row.getVisibleCells().map((cell) => (
                  <Td key={cell.id}>
                    {flexRender(cell.column.columnDef.cell, cell.getContext())}
                  </Td>
                ))}
              </Tr>
            ))}
          </Tbody>
        </Table>
      </TableContainer>
    </Box>
  );
};

@tannerlinsley This behavior seems a little weird to me? Mostly because if I don't pass the types are props, everything works fine. Once I pass the types as generics, it breaks.

csandman commented 2 years ago

@tannerlinsley any is not discouraged. This is the perfect case to use it because you are describing an array of column defs with varying generic types. If you want to avoid it, then don’t pre type your column def array. Simply define an array and fill it with column defs created with the helper. This will result in an array with and inferred type of its contents.

I'm not sure how you can say any is not discouraged when the TypeScript docs explicitly say not to use it:

any

❌ Don't use any as a type unless you are in the process of migrating a JavaScript project to TypeScript. The compiler effectively treats any as "please turn off type checking for this thing". It is similar to putting an @ts-ignore comment around every usage of the variable. This can be very helpful when you are first migrating a JavaScript project to TypeScript as you can set the type for stuff you haven't migrated yet as any, but in a full TypeScript project you are disabling type checking for any parts of your program that use it.

In cases where you don't know what type you want to accept, or when you want to accept anything because you will be blindly passing it through without interacting with it, you can use unknown.

https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#any

And like @firxworx said, even if you avoid typing the array itself when using a columnHelper, you then can't type the props for a table meant for general use correctly.


The part I'm further confused about is the inferred interface that comes from using a column helper with differently typed columns. For example, this is a column def with only string cells:

interface TableRow {
  id: number;
  firstName: string;
  lastName: string;
}

const colHelper = createColumnHelper<TableRow>();

const columns = [
  colHelper.accessor("firstName", {
    header: "Name",
    cell: (info) => info.getValue(),
  }),
  colHelper.accessor("firstName", {
    header: "Name",
    cell: (info) => info.getValue(),
  }),
];

And the inferred type makes sense:

const watchlistIndexTableColumns: ColumnDef<TableRow, string>[]

And in that situation, if type the array to ColumnDef<TableRow, string>[], everything is fine.

Now if I add one column with a number type:

const columns = [
  colHelper.accessor("id", {
    header: "ID",
    cell: (info) => info.getValue(),
  }),
  colHelper.accessor("firstName", {
    header: "Name",
    cell: (info) => info.getValue(),
  }),
  colHelper.accessor("firstName", {
    header: "Name",
    cell: (info) => info.getValue(),
  }),
];

The inferred type completely changes:

const columns: (({
    footer?: ColumnDefTemplate<HeaderContext<TableRow, number>>;
    cell?: ColumnDefTemplate<CellContext<TableRow, number>>;
    meta?: ColumnMeta<...>;
} & ... 6 more ... & StringHeaderIdentifier) | ... 4 more ... | ({
    ...;
} & ... 8 more ... & {
    ...;
}))[]

I'm confused why these are so different. It appears that the ColumnDef types are getting expanded but I can't seem to figure out why. I tried two different strategies of typing the array to see what was possible, and they both provided different results.

The first:

const columns: ColumnDef<TableRow, string | number>[] = [

produced this error:

Type 'ColumnDef<TableRow, number>' is not assignable to type 'ColumnDef<TableRow, string | number>'.

and the second:

const columns: (ColumnDef<TableRow, string> | ColumnDef<TableRow, number>)[] = [

produces this error:

Type 'ColumnDef<TableRow, never>' is not assignable to type 'ColumnDef<TableRow, string> | ColumnDef<TableRow, number>'.

Specifically the second, I'm very confused about, I'm really not sure where the type never is coming into this equation. Especially because if I make the column functions outside of the array and then add them to a typed array, it appears to work fine:

const idCol = colHelper.accessor("id", {
  header: "ID",
  cell: (info) => info.getValue(),
});

const firstNameCol = colHelper.accessor("firstName", {
  header: "Name",
  cell: (info) => info.getValue(),
});

const columns: (ColumnDef<TableRow, string> | ColumnDef<TableRow, number>)[] = [
  idCol,
  firstNameCol,
];

If you could shed some light on why this is happening, I'd definitely appreciate it.

opti21 commented 2 years ago

Seems this hasn't been solved, Going to re-open this to hopefully get some movement on it.

terry-au commented 2 years ago

I am still having the issue on 8.5.13:

type KegLocation = {
  zone: string;
  count: number;
  empty: number;
};

const columnHelper = createColumnHelper<KegLocation>();

const columns: ColumnDef<KegLocation, string | number | null>[] = [
  columnHelper.accessor('zone', {
    cell: (info) => info.renderValue(),
    header: () => <span>Zone</span>,
  }),
  columnHelper.accessor('count', {
    header: () => 'Total',
    cell: (info) => info.renderValue(),
  }),
  columnHelper.accessor('empty', {
    header: () => 'Empty',
    cell: (info) => info.renderValue(),
  }),
];

@znat In your example, null is not mapped to any property in KegLocation. If you re-define the array type as (ColumnDef<KegLocation, string> | ColumnDef<KegLocation, number>)[], it should work.

The inferred type completely changes:

@csandman I don't think it changes. It looks like the TypeScript compiler displays the expanded type when there are overlapping types unions. ColumnDef is an alias for AccessorColumnDef, which is a union of several other types that may be expanded.

image

Example: union-types

Code ```typescript type GenericFoo = { fooValue: Type }; type GenericBar = { barValue: Type }; type GenericBaz = { bazValue: Type }; type GenericFooOrBar = GenericFoo | GenericBar; type GenericFooOrBaz = GenericFoo | GenericBaz; const makeFooOrBar = (val: T): GenericFooOrBar => ({ fooValue: val }); const makeFooOrBaz = (val: T): GenericFooOrBaz => ({ bazValue: val }); const fooBarValues = [ makeFooOrBar(1), makeFooOrBar(2), ] const fooBazValues = [ makeFooOrBaz(1), makeFooOrBaz(2), ] const values = [ makeFooOrBar('test1'), makeFooOrBaz('test1'), ] ```

@opti21 could you please post code to reproduce this issue? I've filed a PR which should fix it. The current release exhibits this bug when using column-helper on type that references itself.

opti21 commented 2 years ago

https://stackblitz.com/edit/github-sq9c9a?file=pages%2Findex.tsx

The stackblitz should still have the original code, looks like it's not putting out the error now on the latest version. So maybe the others having the issue still can post their own stackblitz to see if the error occurs with their code still.

jonahallibone commented 2 years ago

I'd still love to see an example of a table being passed columns as props without using any inside the table component

talatkuyuk commented 2 years ago

Here is my workaround having possible variety of accessor column definition with type.

const columnHelper = createColumnHelper<Person>();

const columns = [
  columnHelper.accessor<"firstName", string>("firstName", {
    cell: (info) => info.getValue(),
    footer: (props) => props.column.id,
  }),
  columnHelper.accessor<(row: Person) => string, string>((row) => row.lastName, {
    id: "lastName",
    cell: (info) => info.getValue(),
    header: () => <span>Last Name</span>,
    footer: (props) => props.column.id,
  }),
  columnHelper.accessor<"age", number>("age", {
    cell: (info) => info.getValue(),
    footer: (props) => props.column.id,
  }),
];
talatkuyuk commented 2 years ago

I'd still love to see an example of a table being passed columns as props without using any inside the table component @jonahallibone

It is viable (I did, and works) (no any !)

jonahallibone commented 2 years ago

@talatkuyuk

  • Create a Custom Table component accepting the table instance as a prop (in a different file and use generic type like TData in order to use for all table instances)

I think ideally the table instance could be formed inside the Custom Table Component. I want to dynamically pass columns to the table / table instance, not pass a table dynamically to a layout. If I am not understanding though I'd love to see a code example!

talatkuyuk commented 2 years ago

@jonahallibone

I have created a discussion https://github.com/TanStack/table/discussions/4510

Let's discuss the proper way for creating Custom Table component issue it in that place; since here opened issue is about columnHelper.accessor Type issue.

KevinVandy commented 2 years ago

For anyone still running into this, make sure you are on at least TypeScript v 4.8 or later. That can clear some instances of these errors.

TanStack tends to use the newer TypeScript features pretty early.

nurbolnygmanov commented 1 year ago

still having the same issue in 2023. is there any solution for: Type 'ColumnDefBase<Person, string> & StringHeaderIdentifier' is not assignable to type 'ColumnDef<Person, unknown>'.

aburkowsky9 commented 1 year ago

@tannerlinsley @nurbolnygmanov Ugly as hell but you can get proper typing here, without widening to any:

Casting is ok here, it will still type check against the output of the accessor method and works similarly to the casting needed with unknown. You can significantly clean this up with a higher order generic as well.

The following are not just raw strings either. Everything is a key of an interface and the existing types will help your IDE provide autocompletion :)

export type PatientRecordColumn<
  // keys are of the interface Patient...
  T extends keyof Patient | undefined = undefined
> = ColumnDef<Patient, T extends keyof Patient ? Patient[T] : Patient>;

const columnHelper = createRecordColumnHelper<Patient>();
  const columns: (
    | PatientRecordColumn
    | PatientRecordColumn<'dob_date'>
    | PatientRecordColumn<'city'>
    | PatientRecordColumn<'insurance'>
    | PatientRecordColumn<'lastContact_timestamp'>
    | PatientRecordColumn<'responseStatus'>
    | PatientRecordColumn<'status'>
  )[] = [
    columnHelper.accessor((row) => row, {
      id: 'name',
      header: () => t('record.name'),
      cell: (info) => {
        const name = `${info.getValue().firstName} ${info.getValue().lastName}`;
        const route = `/patients/${info.getValue().id}`;

        return (
          // exotic components (Box, Link) are not callable, so have to add markup
          // and make sure this file is .tsx
          <Box className="is-flex">
            <Avatar username={name} size={24} />
            <CanConditional
              action="update"
              subject="patients"
              fallback="username">
              <Link
                className="has-text-black is-not-underlined"
                onClick={() => captureInternalNav(route)}
                to={route}>
                <span className="is-md-medium has-ml-8">{name}</span>
              </Link>
            </CanConditional>
          </Box>
        );
      }
    }) as PatientRecordColumn,
    columnHelper.accessor('dob_date', {
      header: () => t('record.age'),
      cell: (info) => (info.getValue() ? getAge(info.getValue()) : '-')
    }) as PatientRecordColumn<'dob_date'>,
    columnHelper.accessor('city', {
      header: () => t('city')
    }) as PatientRecordColumn<'city'>,
    columnHelper.accessor('insurance', {
      header: () => t('record.insurance')
    }) as PatientRecordColumn<'insurance'>,
    columnHelper.accessor('status', {
      header: () => t('patientStatus'),
      cell: (info) =>
        Status({
          color: info.getValue() !== 'Unknown' ? 'green' : 'blue',
          children: t(`statuses.${info.getValue()}`)
        })
    }) as PatientRecordColumn<'status'>,
    columnHelper.accessor('responseStatus', {
      header: () => t('responseStatus'),
      cell: (info) =>
        Status({
          color: info.getValue() !== 'None' ? 'green' : 'yellow',
          children: t(`statuses.${info.getValue()}`)
        })
    }) as PatientRecordColumn<'responseStatus'>,
    columnHelper.accessor('lastContact_timestamp', {
      header: () => t('record.lastContact'),
      cell: (info) => getLastSeenTimeString(info.getValue())
    }) as PatientRecordColumn<'lastContact_timestamp'>
  ];
Danny101201 commented 1 year ago

I fix this error by below :

here is my code

 type User = {
  id: number
  photo?: string | Blob | MediaSource
  name: string
  account: string
  role: string
  email?: string
  remark?: string
  enable: number
}
const COLUMN_IDS: Array<keyof User> = [
  'id',
  'name',
  'account',
  'role',
  'enable',
]
const COLUMNS = COLUMN_IDS.map((id) => userTableColumnHelper.accessor(id, {}))

image

then i check userTableColumnHelper.accessor id type .I have discovered a type inference for the id of a React table, which includes nested prototype types.

image

So i change User photo type to string this error is fixed.

LongJohnSilver1504 commented 1 year ago

Hi there. Any solution for this problem?

basinibi commented 1 year ago

Hi, I have solved same problem by this way

I tried to fix type of data which get from server but that was not solution.

instead, I tried below code and solved.

const table = useReactTable({
      data : alarmData,
      columns : columns,
      getCoreRowModel: getCoreRowModel(),
   })

I hope to this way is going to help you :)

syahmifauzi commented 1 year ago

I'm also facing the same issue with this version: "@tanstack/react-table": "^8.9.3".

Type error: Type instantiation is excessively deep and possibly infinite.

Getting the above error when using this method:

const columnHelper = createColumnHelper<User>()
const columns = [
  columnHelper.accessor('name', {
    header: USER_LABELS.name,
    cell: (info) => info.getValue()
  }),
  columnHelper.accessor('email', {
    header: USER_LABELS.email,
    cell: (info) => info.getValue()
  }),
  columnHelper.accessor('role', {
    header: USER_LABELS.role,
    cell: (info) => info.getValue()
  }),
  columnHelper.accessor('isActivated', {
    header: USER_LABELS.isActivated,
    cell: (info) => (info.getValue() ? 'Active' : 'Not Active')
  })
]

My workaround for now is to use the old ways (no error when building the code):

const columns = [
  { accessorKey: 'name', header: USER_LABELS.name },
  { accessorKey: 'email', header: USER_LABELS.email },
  { accessorKey: 'role', header: USER_LABELS.role },
  {
    accessorFn: (row: User) => (row.isActivated ? 'Active' : 'Not Active'),
    header: USER_LABELS.isActivated
  }
]

Please remind me once the issue is resolved. 😅

vktrl commented 1 year ago

Wrote my own helper for mild type safety and convenience.

const createColumnHelper =
  <TData extends Record<string, unknown>>() =>
  <T extends keyof TData | ((row: TData) => unknown)>(
    accessor: T,
    def: ColumnDef<TData, T extends (row: TData) => unknown ? ReturnType<T> : T extends keyof TData ? TData[T] : never>
  ) => {
    return Object.assign(
      def,
      typeof accessor === 'string'
        ? { accessorKey: accessor, accessorFn: undefined }
        : { accessorKey: undefined, accessorFn: accessor }
    );
  };

I just started using the library and maybe I'm missing something, but I don't understand the reason for the complexity and the lack resulting types of the helper provided by the library.