Skip to content

Suggestion: Clarify typing with computed<T> vs return annotation (excess property checks) #3286

@CesarWatrin

Description

@CesarWatrin

[docs] Clarify typing with computed<T> vs return annotation (excess property checks)

Summary

The docs recommend typing a computed value with a generic:

const double = computed<number>(() => {
  return count.value * 2
})

This works, but there is a subtle TypeScript behavior that can surprise users:

  • With computed<Foo>(...), TypeScript only checks that the return value is assignable to Foo.
  • Extra properties not present in Foo are silently allowed because excess property checks do not apply when inferring into a generic.
  • Using a function return type annotation (): Foo => ... does enforce excess property checks and will reject extra properties.

Minimal reproduction

import { computed } from 'vue'

interface FooBarInterface {
  foo: boolean
}

// 1) Generic parameter (loose): allows extra props
const first = computed<FooBarInterface>(() => ({
  foo: false,
  bar: 'test', // ✅ no error (extra property allowed)
}))

// 2) Return annotation (strict): rejects extra props
const second = computed(
  (): FooBarInterface => ({
    foo: false,
    bar: 'test', // ❌ TS2353: Object literal may only specify known properties
  }),
)

Strict alternative with satisfies

To keep the concise generic style while getting strictness, suggest the satisfies operator:

const third = computed(() => ({
  foo: false,
  bar: 'test', // ❌ error: 'bar' is not in FooBarInterface
} satisfies FooBarInterface))

This enforces that the object conforms exactly to FooBarInterface without allowing extra properties.

Why this happens (TypeScript behavior)

This is not a Vue/runtime issue but a well-known TypeScript typing nuance:

  • Generics (computed<Foo>(...)) behave like a constraint: the inferred return just needs to be assignable to Foo. Excess property checks do not run for values inferred into a generic parameter.
  • Explicit return annotations ((): Foo => ...) do trigger excess property checks on object literals, so extra fields are rejected.

Because computed is a generic helper, this difference is unavoidable at the type-system level.

Proposed docs improvement

In the “Typing computed()” section, add a short note warning about this difference and showing the stricter alternatives.

Environment

  • Vue: 3.5.18
  • TypeScript: 5.9.2
  • Nuxt: 4.0.3

This request is strictly about improving documentation and developer ergonomics.

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