Skip to content

[Discussion] Decide callback typing policy across hooks for v1 #563

Closed

Description

@GamjaIsMine02

Describe the issue

Following up on #562 and the maintainer's request, this issue proposes deciding the callback typing policy for v1 across the related hooks.

On main, usePreservedCallback defaults its argument tuple to any[]:

function usePreservedCallback<
  Arguments extends any[] = any[],
  ReturnValue = unknown
>(callback: (...args: Arguments) => ReturnValue)

Explicitly typed callback parameters retain their inferred types. When a parameter has no other type information, the current default allows it to be any. This makes calls convenient, but also allows unchecked property access.

#562 proposed changing the argument tuple constraint and default to unknown[]. As pointed out in review, that breaks existing calls:

usePreservedCallback((e) => e.target.value);
// Current typing: accepted.
// Proposed unknown[] default: TS18046, 'e' is of type 'unknown'.

The review explains that the loose typing is intentional and cites similar choices in Radix, Mantine, ahooks and es-toolkit. It also asks that any breaking change target v1-base.

Scope

Review the policy together for:

  • usePreservedCallback
  • useDebounce / useThrottle
  • useDebouncedCallback / useThrottledCallback, including their v1 signatures
  • useCallbackOncePerRender / useCallbackOnce

This should account for the debounce/throttle consolidation in #537 and the CallbackOnce rename in #542 / #545.

Questions to resolve for v1

  • Keep permissive any defaults for callbacks without parameter annotations, or require explicit typing/narrowing through unknown defaults?
  • If we keep any, should the docs explain why it is intentional and when callers should annotate parameters?
  • If we change the types, what signatures preserve inference for explicitly typed callbacks while applying the chosen policy consistently?

Before implementing a change, compare type-checking examples for untyped and typed callbacks, optional/rest parameters, generic callbacks, and async return values where supported. Check each hook's existing return contract separately.

Additional context

This is a request to agree on the v1 policy; replacing any with unknown is one option under discussion. Any resulting breaking implementation should target v1-base and include migration examples.

Related: #562, #550, #537, #542, #545.

Activity

  1. hyesungoh commented on Oct 15, 2026

    @hyesungoh
    Member

    Thank you for write it

    I want to hear your personal though
    which way would you go, and why?

  2. GamjaIsMine02 commented on Oct 15, 2026

    @GamjaIsMine02
    Author

    I think I opened this issue a bit too quickly. I’ll take some time to think through the trade-offs and put together a clearer proposal before opening it again.

  3. hyesungoh commented on Oct 15, 2026

    @hyesungoh
    Member

    please feel free to open issues 🙏 thank you for your attention

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions