Skip to content

Conversation

hodimbokom
Copy link

@hodimbokom hodimbokom commented Sep 9, 2025

Description

With vueCompilerOptions.strictTemplates: true, adding any event handler to RouterLink (e.g. @focus) triggers a TS error, even though the handler works at runtime (see #2484).

Solution

  • Make RouterLink generic over custom to preserve anchor event/attrs when custom !== true and exclude them when custom === true.
  • Add Omit<AnchorHTMLAttributes, 'href'> so href stays disallowed (we derive it from to).
  • No runtime changes, only types.

Tests

  • Add dts tests to ensure:

    • event handlers and anchor attrs are allowed when not custom
    • href is not allowed
    • anchor attrs/handlers are disallowed when custom is true
  • All tests pass:

    • pnpm -C packages/router build && pnpm -C packages/router build:dts
    • pnpm -C packages/router test:types
    • pnpm -C packages/router test (coverage/unit/e2e)
  • Locally verified with vue-tsc --noEmit and strictTemplates: true on an SFC.

Links

Closes #2484


Also happy to adjust if maintainers prefer another approach.

Summary by CodeRabbit

  • New Features
    • Improved TypeScript typings for RouterLink to better reflect usage modes. When not using custom, standard anchor attributes (e.g., target, rel) and native events are supported while href remains disallowed. When using custom, native anchor events are restricted. No runtime changes.
  • Tests
    • Added type-checking tests to validate the updated RouterLink typings and ensure correct allowances and errors across modes.

Copy link

netlify bot commented Sep 9, 2025

Deploy Preview for vue-router canceled.

Name Link
🔨 Latest commit 9248c9f
🔍 Latest deploy log https://app.netlify.com/projects/vue-router/deploys/68c018d953cc0500086b3f67

Copy link
Contributor

coderabbitai bot commented Sep 9, 2025

Walkthrough

TypeScript typings for RouterLink were refactored to be generic, differentiating prop shapes when custom is enabled or not. Anchor HTML attributes (excluding href) and DOM events are now typed for non-custom usage. Corresponding DTS tests were added to validate allowed/forbidden props under both modes. No runtime changes.

Changes

Cohort / File(s) Summary
RouterLink typing update
packages/router/src/RouterLink.ts
Introduced LinkAnchorAttrs and generic RouterLinkTypedProps; updated exported _RouterLinkI constructor to be generic to select prop shapes based on custom flag; imported AnchorHTMLAttributes; no runtime logic changes.
DTS tests for props and events
packages/router/test-dts/components.test-d.tsx
Added type-check tests ensuring anchor attributes and events are allowed when not custom; ensured href is disallowed; verified certain events (e.g., onFocus) are disallowed when custom is set.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Pre-merge checks (5 passed)

✅ Passed checks (5 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly summarizes the primary change, indicating that RouterLink now allows event handlers under strictTemplates and references the related issue, aligning with the changeset’s main focus.
Linked Issues Check ✅ Passed The changes directly address issue #2484 by enabling event handlers on RouterLink under strictTemplates and providing tests that validate the expected behavior, fulfilling the linked issue’s objectives.
Out of Scope Changes Check ✅ Passed All modifications are confined to RouterLink typings and tests within the router package to implement the intended fix, without unrelated or extraneous changes.
Description Check ✅ Passed The description clearly outlines the problem under strictTemplates, the types-only solution, and accompanying tests, demonstrating direct relevance to the changeset.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

Poem

I nibble on types like clover so green,
Tuning props where anchors have been.
Custom or not? I twitch and infer—
Events may scurry, href won’t occur.
Tests hop in rows, crisp as the dew—
A tighter trail for links to chew. 🐇✨

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Contributor

@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: 0

🧹 Nitpick comments (3)
packages/router/src/RouterLink.ts (2)

1-31: Prefer type-only import for AnchorHTMLAttributes to avoid any runtime import risk

Move AnchorHTMLAttributes to a separate type-only import for consistency and to keep bundlers from attempting to load it at runtime.

-import {
+import {
   defineComponent,
   h,
   PropType,
   inject,
   computed,
   reactive,
   unref,
   VNode,
   UnwrapRef,
   VNodeProps,
   AllowedComponentProps,
   ComponentCustomProps,
   getCurrentInstance,
   watchEffect,
   // this is a workaround for https://github.com/microsoft/rushstack/issues/1050
   // this file is meant to be prepended to the generated dist/src/RouterLink.d.ts
   // @ts-ignore
   ComputedRef,
   // @ts-ignore
   DefineComponent,
   // @ts-ignore
   RendererElement,
   // @ts-ignore
   RendererNode,
   // @ts-ignore
   ComponentOptionsMixin,
   MaybeRef,
-  AnchorHTMLAttributes,
 } from 'vue'
+import type { AnchorHTMLAttributes } from 'vue'

369-379: Typing strategy achieves goal; verify behavior with dynamic boolean custom bindings

The conditional props cleanly allow anchor events/attrs when custom !== true and exclude them when custom === true, and href is properly omitted. One edge case: <RouterLink :custom="someBool" ...> (or TSX custom={someBool} where someBool: boolean) may not be assignable to either branch of the union (true vs false|undefined), causing a TS error. If that’s acceptable, consider documenting it; otherwise, we may need a broader strategy (tradeoff: allowing dynamic booleans typically re-allows anchor attrs).

You can extend the dts test with:

// dynamic boolean: today this likely errors (by design). Decide if we want to support it.
const isCustom: boolean = Math.random() > 0.5
// @ts-expect-error: dynamic boolean not supported by narrowed props
expectError(<RouterLink to="/" custom={isCustom} onFocus={() => {}} />)
// explicit false should allow anchor attrs/handlers
expectTypeOf<JSX.Element>(<RouterLink to="/" custom={false} onFocus={() => {}} target="_blank" />)
// explicit true should forbid anchor attrs/handlers
// @ts-expect-error
expectError(<RouterLink to="/" custom={true} onFocus={() => {}} />)
packages/router/test-dts/components.test-d.tsx (1)

30-40: Great coverage; add a couple of assertions for explicit custom={false} and ARIA/data fallthrough

Tests capture the core behaviors. Consider adding:

  • acceptance of custom={false} with events/attrs
  • an ARIA/data attribute to ensure broad fallthrough coverage
// allows when explicitly false
expectTypeOf<JSX.Element>(<RouterLink to="/" custom={false} onFocus={() => {}} target="_blank" />)
// allows ARIA/data attrs
expectTypeOf<JSX.Element>(<RouterLink to="/" aria-label="Home" data-testid="rl" />)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7ce331 and 9248c9f.

📒 Files selected for processing (2)
  • packages/router/src/RouterLink.ts (2 hunks)
  • packages/router/test-dts/components.test-d.tsx (1 hunks)
🔇 Additional comments (1)
packages/router/src/RouterLink.ts (1)

381-383: Generic constructor change LGTM

This preserves inference in JSX while enabling the conditional props. No runtime impact; aligns with the stated objective.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Using event handler with RouterLink combined with vueCompilerOptions strictTemplates gives TS error
1 participant