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.24k stars 3.08k forks source link

Vue Query's `onPaginationChange` has incorrect union type/unexpected behavior, unexpected API, "This expression is not callable. Not all constituents of type "Updater<PaginationState>" are callable" #5305

Open braden-w opened 9 months ago

braden-w commented 9 months ago

TanStack Table version

v8.11.7

Framework/Library version

Vue v3.4.15

Describe the bug and the steps to reproduce it

The onPaginationChange function in useVueTable is typed as OnChangeFn<PaginationState>, but the actual type seems to always be

(updater: (args: PaginationState) => PaginationState) => void

In my the reproduction, you'll see that the printed value for updater is

(old) => {
        let newState = functionalUpdate(updater, old);
        return newState;
      }

if you click a button to update the pagination.

This type, (updater: (args: PaginationState) => PaginationState) => void, is different than its provided unioned definition, and it's causing a type error (could I get some clarification if I'm wrong here?).

The Type Error

If you see the images, I'm getting various Typescript errors in my editor. In my first picture, it insists that my type for onPaginationChange is incorrect, despite me being sure that the implementation is working. In the second picture, when I'm inlining the function, I'm getting "This expression is not callable. Not all constituents of type "Updater" are callable". This seems to be due to the definition of

export type OnChangeFn<T> = (updaterOrValue: Updater<T>) => void;
export type Updater<T> = T | ((old: T) => T)

the Union type of type Updater<T> = T | ((old: T) => T) seems to be causing this constant Typescript error, and I have been unable to resolve it without a Type assertion.

Separate Issue: onPaginationChange type was not what I expected, and the docs compounded this

I expected the onPaginationChange function to have a type

(newPaginationState: PaginationState, oldPaginationState: PaginationState) => void

rather than of type

(updater: (args: PaginationState) => PaginationState) => void

to more closely mimic the style of Vue's watch function.

Instead, I'm calling pagination.value = updatePagination(pagination.value) inside onPaginationChange, which feels somewhat cumbersome and unintuitive.

The ambiguity of current documentation and the naming of the Updater type suggest a more straightforward update mechanism, akin to typical state updates in Vue. This discrepancy between the expected and actual behavior can lead to misunderstandings about how to properly use the onPaginationChange function. In the docs, it is said:

If this function is provided, it will be called when the pagination state changes and you will be expected to manage the state yourself. You can pass the managed state back to the table via the tableOptions.state.pagination option.

This creates ambiguity and potentially misleads new users. More information on how to pass the managed state back in the table would be appreciated (such as showing an example onPaginationChange function.

Your Minimal, Reproducible Example - (Sandbox Highly Recommended)

https://stackblitz.com/edit/tanstack-table-rsbmuy?file=src%2FApp.vue

Screenshots or Videos (Optional)

CleanShot 2024-01-27 at 14 28 56 CleanShot 2024-01-27 at 14 29 22

Do you intend to try to help solve this bug with your own PR?

Maybe, I'll investigate and start debugging

Terms & Code of Conduct

ammuench commented 8 months ago

+1 to this issue. docs are incredible unclear. @braden-w's solution worked but required me to pass in a ts-ignore

minhypro commented 2 months ago

It seems like this function behaves similarly to setState in that the argument can either be a function or the value itself. At the moment, I'm attempting to handle the typing this way. Pass the old value to updateOrValue then get the updated value

setPagination: updateOrValue => {
   const oldPagination = get().pagination;
   const updatedPagination = updateOrValue instanceof Function ? updateOrValue(oldPagination) : updateOrValue;
   set({ pagination: updatedPagination });
}
Ram4GB commented 1 month ago

It seems we already have your case in our docs, you can checkout more information in here

Reference: https://github.com/TanStack/table/discussions/4005