]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(compiler-vapor): wrap event handler in parentheses for TSExpression (#14061)
authorzhiyuanzmj <260480378@qq.com>
Mon, 10 Nov 2025 01:23:23 +0000 (09:23 +0800)
committerGitHub <noreply@github.com>
Mon, 10 Nov 2025 01:23:23 +0000 (09:23 +0800)
packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap
packages/compiler-vapor/__tests__/transforms/vOn.spec.ts
packages/compiler-vapor/src/generators/event.ts
packages/compiler-vapor/src/generators/expression.ts

index 8423cb2ed5588d1f5a2c23b05820ce481c229778..52c47030a80352d85070260a2c75bed55e453a95 100644 (file)
@@ -145,7 +145,7 @@ _delegateEvents("click")
 
 export function render(_ctx, $props, $emit, $attrs, $slots) {
   const n0 = t0()
-  n0.$evtclick = e => _ctx.handleClick(e)
+  n0.$evtclick = e => (_ctx.foo[_ctx.handleClick] as any)(e)
   return n0
 }"
 `;
index 414ea0e298b70de8b7928609faf41096e59c578e..3402f577f0657c768dab35b1ab5f6348d0290778 100644 (file)
@@ -685,7 +685,7 @@ describe('v-on', () => {
 
   test('expression with type', () => {
     const { code } = compileWithVOn(
-      `<div @click="(<number>handleClick as any)"></div>`,
+      `<div @click="foo[handleClick] as any"></div>`,
       {
         bindingMetadata: {
           handleClick: BindingTypes.SETUP_CONST,
@@ -693,7 +693,9 @@ describe('v-on', () => {
       },
     )
     expect(code).matchSnapshot()
-    expect(code).include('n0.$evtclick = e => _ctx.handleClick(e)')
+    expect(code).include(
+      'n0.$evtclick = e => (_ctx.foo[_ctx.handleClick] as any)(e)',
+    )
   })
 
   test('component event with special characters', () => {
index cfb47b6118456a788a78f59478a7664a40543d37..076d94357063c32484ad3789e7d262c531403d10 100644 (file)
@@ -1,6 +1,7 @@
 import {
   BindingTypes,
   type SimpleExpressionNode,
+  TS_NODE_TYPES,
   isFnExpression,
   isMemberExpression,
 } from '@vue/compiler-dom'
@@ -126,7 +127,14 @@ export function genEventHandler(
         // non constant, wrap with invocation as `e => foo.bar(e)`
         // when passing as component handler, access is always dynamic so we
         // can skip this
-        handlerExp = [`e => `, ...handlerExp, `(e)`]
+        const isTSNode = value.ast && TS_NODE_TYPES.includes(value.ast.type)
+        handlerExp = [
+          `e => `,
+          isTSNode ? '(' : '',
+          ...handlerExp,
+          isTSNode ? ')' : '',
+          `(e)`,
+        ]
       }
     } else if (isFnExpression(value, context.options)) {
       // Fn expression: @click="e => foo(e)"
index c70d4a566135017d89255c5f332440c29c658de5..2faaa13ff5dbefac18bc87c45920bc3c93770e1a 100644 (file)
@@ -10,7 +10,6 @@ import {
   NewlineType,
   type SimpleExpressionNode,
   type SourceLocation,
-  TS_NODE_TYPES,
   advancePositionWithClone,
   createSimpleExpression,
   isInDestructureAssignment,
@@ -64,7 +63,6 @@ export function genExpression(
   let hasMemberExpression = false
   if (ids.length) {
     const [frag, push] = buildCodeFragment()
-    const isTSNode = ast && TS_NODE_TYPES.includes(ast.type)
     ids
       .sort((a, b) => a.start! - b.start!)
       .forEach((id, i) => {
@@ -73,11 +71,8 @@ export function genExpression(
         const end = id.end! - 1
         const last = ids[i - 1]
 
-        if (!(isTSNode && i === 0)) {
-          const leadingText = content.slice(last ? last.end! - 1 : 0, start)
-          if (leadingText.length) push([leadingText, NewlineType.Unknown])
-        }
-
+        const leadingText = content.slice(last ? last.end! - 1 : 0, start)
+        if (leadingText.length) push([leadingText, NewlineType.Unknown])
         const source = content.slice(start, end)
         const parentStack = parentStackMap.get(id)!
         const parent = parentStack[parentStack.length - 1]
@@ -103,7 +98,7 @@ export function genExpression(
           ),
         )
 
-        if (i === ids.length - 1 && end < content.length && !isTSNode) {
+        if (i === ids.length - 1 && end < content.length) {
           push([content.slice(end), NewlineType.Unknown])
         }
       })