Skip to content

fix(types): narrow useAttrs class/style typing for TSX#14492

Merged
edison1105 merged 2 commits intomainfrom
edison/fix/14489
Mar 9, 2026
Merged

fix(types): narrow useAttrs class/style typing for TSX#14492
edison1105 merged 2 commits intomainfrom
edison/fix/14489

Conversation

@edison1105
Copy link
Copy Markdown
Member

@edison1105 edison1105 commented Feb 28, 2026

close #14489

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced TypeScript support for component attributes with new Attrs and AllowedAttrs type definitions.
    • Improved type safety for JSX class and style attributes in TSX components.
    • Extended attribute support for non-declared properties with better type inference.
  • Tests

    • Added comprehensive type tests for attribute passthrough behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 28, 2026

📝 Walkthrough

Walkthrough

This PR fixes a TypeScript regression by introducing a new extendable Attrs type that properly types $attrs.class and $attrs.style attributes. The AllowedAttrs interface is defined in runtime-core and augmented in runtime-dom with ClassValue and StyleValue typing, enabling correct type inference when passing attributes to JSX elements.

Changes

Cohort / File(s) Summary
Type Infrastructure
packages/runtime-core/src/component.ts
Added AllowedAttrs interface and Attrs type (combining Data & AllowedAttrs). Updated SetupContext.attrs to use Attrs and cast attrsProxy appropriately.
Public API Updates
packages/runtime-core/src/componentPublicInstance.ts, packages/runtime-core/src/index.ts
Updated ComponentPublicInstance.$attrs to use Attrs type and re-exported Attrs and AllowedAttrs from public API.
Runtime-DOM Augmentation
packages/runtime-dom/src/index.ts
Extended AllowedAttrs interface with optional class?: ClassValue and style?: StyleValue properties.
Type Tests
packages-private/dts-test/tsx.test-d.tsx
Added useAttrs() import and test cases validating passthrough of attrs.class and attrs.style to JSX elements.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 Type attrs now with care,
ClassValue floats through the air,
No more unknown in sight,
JSX bindings shine so bright,
Vue's regression takes flight! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title mentions 'narrow' but the PR objectives describe 'widening' ClassValue type for broader compatibility with $attrs values. Update the PR title to reflect the actual intent: 'fix(types): widen ClassValue to accept fallthrough attrs values' to match the PR description and objectives.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR successfully addresses issue #14489 by broadening the type system to accept $attrs.class and $attrs.style in JSX, resolving the regression where assigning $attrs.class to :class produced type errors.
Out of Scope Changes check ✅ Passed All changes are scoped to type definitions and test augmentation directly related to fixing the $attrs.class/$attrs.style typing issue; no unrelated functionality changes were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch edison/fix/14489

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 104 kB 39.3 kB 35.4 kB
vue.global.prod.js 162 kB 59.3 kB 52.8 kB

Usages

Name Size Gzip Brotli
createApp (CAPI only) 47.8 kB 18.6 kB 17.1 kB
createApp 56 kB 21.7 kB 19.8 kB
createSSRApp 60.2 kB 23.4 kB 21.4 kB
defineCustomElement 61.6 kB 23.4 kB 21.4 kB
overall 70.4 kB 27 kB 24.6 kB

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented Feb 28, 2026

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@14492
npm i https://pkg.pr.new/@vue/compiler-core@14492
yarn add https://pkg.pr.new/@vue/compiler-core@14492.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@14492
npm i https://pkg.pr.new/@vue/compiler-dom@14492
yarn add https://pkg.pr.new/@vue/compiler-dom@14492.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@14492
npm i https://pkg.pr.new/@vue/compiler-sfc@14492
yarn add https://pkg.pr.new/@vue/compiler-sfc@14492.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@14492
npm i https://pkg.pr.new/@vue/compiler-ssr@14492
yarn add https://pkg.pr.new/@vue/compiler-ssr@14492.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@14492
npm i https://pkg.pr.new/@vue/reactivity@14492
yarn add https://pkg.pr.new/@vue/reactivity@14492.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@14492
npm i https://pkg.pr.new/@vue/runtime-core@14492
yarn add https://pkg.pr.new/@vue/runtime-core@14492.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@14492
npm i https://pkg.pr.new/@vue/runtime-dom@14492
yarn add https://pkg.pr.new/@vue/runtime-dom@14492.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@14492
npm i https://pkg.pr.new/@vue/server-renderer@14492
yarn add https://pkg.pr.new/@vue/server-renderer@14492.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@14492
npm i https://pkg.pr.new/@vue/shared@14492
yarn add https://pkg.pr.new/@vue/shared@14492.tgz

vue

pnpm add https://pkg.pr.new/vue@14492
npm i https://pkg.pr.new/vue@14492
yarn add https://pkg.pr.new/vue@14492.tgz

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@14492
npm i https://pkg.pr.new/@vue/compat@14492
yarn add https://pkg.pr.new/@vue/compat@14492.tgz

commit: fdc05ce

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages-private/dts-test/tsx.test-d.tsx (1)

64-67: Add a negative assertion to prevent over-widening class typing.

Please pair this with a // @ts-expect-error`` case (e.g. class={123}) so the suite also enforces that invalid class values remain rejected.

Suggested dts-test addition
 // allow class passthrough from attrs
 const attrs = useAttrs()
 expectType<JSX.Element>(<div class={attrs.class} />)
+
+// `@ts-expect-error` invalid class value should still be rejected
+;<div class={123} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages-private/dts-test/tsx.test-d.tsx` around lines 64 - 67, Add a
negative type assertion to the existing test that verifies class passthrough
from useAttrs does not over-widen the type: after the positive
expectType<JSX.Element>(<div class={attrs.class} />) add a failing case
annotated with // `@ts-expect-error` that tries an invalid class value (for
example class={123}) to ensure the compiler still rejects
non-string/non-string[] class values; update the tsx.test-d.tsx around the
useAttrs/attrs assertions so both the valid passthrough and the explicit
invalid-case are present.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages-private/dts-test/tsx.test-d.tsx`:
- Around line 64-67: Add a negative type assertion to the existing test that
verifies class passthrough from useAttrs does not over-widen the type: after the
positive expectType<JSX.Element>(<div class={attrs.class} />) add a failing case
annotated with // `@ts-expect-error` that tries an invalid class value (for
example class={123}) to ensure the compiler still rejects
non-string/non-string[] class values; update the tsx.test-d.tsx around the
useAttrs/attrs assertions so both the valid passthrough and the explicit
invalid-case are present.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea24576 and b1b1925.

📒 Files selected for processing (2)
  • packages-private/dts-test/tsx.test-d.tsx
  • packages/runtime-dom/src/jsx.ts

@edison1105 edison1105 added ready to merge The PR is ready to be merged. scope: types 🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. labels Feb 28, 2026
@ZacharyBear
Copy link
Copy Markdown
Contributor

LGTM

But I'd like to expand the Data type, which is returned by useAttrs().
Now it's defined as:

type Data = Record<string, unknown>;

See:


export type Data = Record<string, unknown>

@edison1105 edison1105 changed the title fix(types): widen ClassValue to accept fallthrough attrs values fix(types): narrow useAttrs class/style typing for TSX Mar 9, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/runtime-core/src/component.ts`:
- Around line 101-104: Add an inline Biome rule ignore immediately above the
export of the empty AllowedAttrs interface to suppress the noEmptyInterface
rule; keep the exported declaration export interface AllowedAttrs {} as the
public extension point and place a single-line or block ignore (e.g. /*
biome-ignore noEmptyInterface */) directly above that declaration so the
intentional empty interface used for declaration-merging is not flagged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7427b117-a9d7-4e7f-8e90-560e76c472eb

📥 Commits

Reviewing files that changed from the base of the PR and between b1b1925 and fc8d8c8.

📒 Files selected for processing (5)
  • packages-private/dts-test/tsx.test-d.tsx
  • packages/runtime-core/src/component.ts
  • packages/runtime-core/src/componentPublicInstance.ts
  • packages/runtime-core/src/index.ts
  • packages/runtime-dom/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages-private/dts-test/tsx.test-d.tsx

Comment thread packages/runtime-core/src/component.ts
@edison1105
Copy link
Copy Markdown
Member Author

/ecosystem-ci run

@vuejs vuejs deleted a comment from edison1105 Mar 9, 2026
@vuejs vuejs deleted a comment from edison1105 Mar 9, 2026
@edison1105 edison1105 merged commit bbb8977 into main Mar 9, 2026
15 of 16 checks passed
@edison1105 edison1105 deleted the edison/fix/14489 branch March 9, 2026 06:23
@vue-bot
Copy link
Copy Markdown
Contributor

vue-bot commented Mar 9, 2026

📝 Ran ecosystem CI: Open

suite result latest scheduled
primevue success success
quasar success success
vite-plugin-vue success success
vant success success
vuetify success success
router success success
test-utils success success
radix-vue success success
nuxt success success
vueuse success success
pinia success success
vue-i18n success success
language-tools failure failure
vitepress success success
vue-simple-compiler success success
vue-macros success success

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

Labels

🔨 p3-minor-bug Priority 3: this fixes a bug, but is an edge case that only affects very specific usage. ready to merge The PR is ready to be merged. scope: types

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3.5.29 regression: type error when assigning $attrs.class to :class

3 participants