Skip to content

[vue-query]: UseMutationReturnType default names unexported MutationResult (TS2883) 馃馃馃聽#11769

Description

@earthabledev

Describe the bug

@tanstack/vue-query 5.104.0 publishes UseMutationReturnType with a default type argument that names MutationResult. MutationResult is not exported. A project that emits declarations (composite: true, which implies declaration) cannot name the inferred return type of an exported wrapper around useMutation.

TypeScript reports:

TS2883: The inferred type of 'useExampleMutation' cannot be named without a reference to 'MutationResult' from '@tanstack/vue-query/build/modern/useMutation'. This is likely not portable. A type annotation is necessary.

@ts-expect-error on the export suppresses TS2883 and is then reported as unused (TS2578). The diagnostic is produced during declaration emit, after TypeScript decides whether the directive was used. @ts-ignore is not a substitute we can use.

5.91.2 still declares the private alias, but the published .d.ts re-exports the function with export { type UseMutationReturnType, useMutation }. That form typechecks. 5.104.0 uses export type UseMutationReturnType<..., TResult = MutationResult<...>> and export declare function useMutation, and declaration emit then has to name MutationResult.

Related, and not the same report:

Your minimal, reproducible example

No hosted sandbox. vue-tsc --build against a composite project is the reproduction. The three files below are complete.

package.json

{
  "private": true,
  "type": "module",
  "dependencies": {
    "@tanstack/vue-query": "5.104.0",
    "vue": "3.5.43"
  },
  "devDependencies": {
    "typescript": "6.0.3",
    "vue-tsc": "3.3.11"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "composite": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "strict": true,
    "target": "esnext",
    "skipLibCheck": true
  },
  "include": ["src"]
}

src/useExampleMutation.ts

import { useMutation } from '@tanstack/vue-query'

export const useExampleMutation = () => {
  return useMutation({
    mutationFn: async () => 'ok',
  })
}

Steps to reproduce

  1. Install the three files above.
  2. Run pnpm exec vue-tsc --build --noEmit --force.
  3. See TS2883 on useExampleMutation.
  4. Repeat with @tanstack/vue-query 5.91.2. The same command exits 0.

Expected behavior

An exported wrapper around useMutation should typecheck under composite / declaration emit without naming a private type. MutationResult should be exported, or it should not appear as a default type argument on the public UseMutationReturnType.

How often does this bug happen?

Every time

Platform

  • OS: macOS
  • Browser: not applicable (vue-tsc)
  • Version: TypeScript 6.0.3, vue-tsc 3.3.11, Vue 3.5.43

Tanstack Query adapter

vue-query

TanStack Query version

5.104.0 (does not reproduce on 5.91.2)

TypeScript version

6.0.3

Additional context

Annotating the wrapper as UseMutationReturnType<TData, TError, TVariables, unknown> is not assignable to the value useMutation returns, because that value is instantiated with the private fifth type argument. The annotation only typechecks if the consumer copies MutationResult (DistributiveOmit<MutationObserverResult<...>, 'mutate' | 'reset'>). That copy has to stay in sync with an unexported alias.

Activity

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