Skip to content

vue 2.7 template type check for inline function #1654

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
troy351 opened this issue Jul 29, 2022 · 26 comments
Closed

vue 2.7 template type check for inline function #1654

troy351 opened this issue Jul 29, 2022 · 26 comments
Labels
question Further information is requested upstream

Comments

@troy351
Copy link

troy351 commented Jul 29, 2022

I'm using vue 2.7 with TypeScript

<template>
<div>
  <div v-for="(item, index) in arr" :key="index">
    <el-input type="text" :value="item" @input="(val) => doSomething(index, val)" />
  </div>
</div>
</template>

As code shown above, the problem is @input="(val) => doSomething(index, val)" part.

  • ts error Parameter 'val' implicitly has an 'any' type. since noImplicitAny in tsconfig was enabled
  • Vue 2.7 doesn't support ts syntax in template as the CHANGELOG said

    ❌ TypeScript syntax in template expressions (incompatible w/ Vue 2 parser)

  • ts-ignore will causing prettier add leading semi colon which is not valid attribute value
    @input="
     //@ts-ignore
     ;(val) => doSomething(index, val)
    "
    
  • Since val is an argument so asAny magic won't work

Any workaround I can do?

@troy351
Copy link
Author

troy351 commented Aug 1, 2022

Solved by disabling both ts and prettier

@input="
 // @ts-ignore
 // prettier-ignore
 (val) => doSomething(index, val)
"

this method is not worked, prefer the below one

@troy351 troy351 closed this as completed Aug 1, 2022
@lsdsjy
Copy link
Contributor

lsdsjy commented Aug 1, 2022

I think it shouldn't trigger the noImplicitAny error because TS has contextual typing which would infer val as Event since input is of type ((payload: Event) => void) | undefined. What do you see when you hover over input?

@troy351
Copy link
Author

troy351 commented Aug 1, 2022

It's just a demo. In the actual case, <input> could be any custom component or third party component which would unlikely infer val

@lsdsjy
Copy link
Contributor

lsdsjy commented Aug 1, 2022

I see. Instead of @ts-ignore you can use something similar to the asAny trick:

@input="anyFn((val) => doSomething(index, val))"

, where const anyFn = (fn: (val: any) => any) => fn. And you can annotate it more accurately if you want.

@troy351
Copy link
Author

troy351 commented Aug 1, 2022

Oh, I didn't think of it. Thanks!

@troy351
Copy link
Author

troy351 commented Aug 8, 2022

@input="anyFn((val) => doSomething(index, val))" will compile to

on: {
    "command": function($event) {
        _vm.anyFn((val) => _vm.doSomething(index, val))
    }
}
// which should be 
on: {
    "command": (val) => _vm.doSomething(index, val)
}

@troy351 troy351 reopened this Aug 8, 2022
@lsdsjy
Copy link
Contributor

lsdsjy commented Aug 8, 2022

Try const anyFn = function (fn: (val: any) => any) { fn.apply(this, arguments) }?

@troy351
Copy link
Author

troy351 commented Aug 8, 2022

It won't work, val should be directly declared as an argument of command handler function, or it won't be reached

@lsdsjy
Copy link
Contributor

lsdsjy commented Aug 8, 2022

Isn't $event equal to val, both representing the command event's payload?

@troy351
Copy link
Author

troy351 commented Aug 8, 2022

You are right, but again, real world case is mush more complicated, like multiple arguments

I ended up modify the vue source code to make it recognize the leading comment as a function expression

change this line to const fnExpRE = /^(\/\/.*\s*)*(([\w$_]+|\([^)]*?\))\s*=>|function(?:\s+[\w$]+)?\s*\()/

Considering whether to make it a PR or not

@xiaoxiangmoe
Copy link
Collaborator

try use

<template>
<div v-for="(item, index) in arr" :key="index">
  <input type="text" :value="item" @input="doSomething(index, $event)" />
</div>
</template>

@troy351
Copy link
Author

troy351 commented Aug 31, 2022

input could be el-input or other custom components which doesn't support $event

@xiaoxiangmoe
Copy link
Collaborator

Why they don't support $event?

@troy351
Copy link
Author

troy351 commented Aug 31, 2022

$emit support multiple arguments, and $event only got one. Works for most cases though

@xiaoxiangmoe
Copy link
Collaborator

Maybe you should create a bug in prettier —— add leading semi colon will break vue event listener

@troy351
Copy link
Author

troy351 commented Aug 31, 2022

Not only the leading semi problem.
As the previous comment said, even I add both ts-ignore and prettier-ignore, vue template compiler won't treat leading comment (i.e. //) function expression as a function expression due to the regular expression test

@xiaoxiangmoe
Copy link
Collaborator

Maybe also create an issue for vue 2.7 and vue 3.

@troy351
Copy link
Author

troy351 commented Aug 31, 2022

That's what I'm thinking about.

@johnsoncodehk
Copy link
Member

johnsoncodehk commented Sep 22, 2022

LGTM:
螢幕截圖 2022-09-22 16 12 03

Can you reproduce this problem in https://github.com/johnsoncodehk/volar-starter/tree/vue2?

@troy351
Copy link
Author

troy351 commented Sep 26, 2022

My bad, I've updated code snippet, using <el-input>(which is from element-ui) instead of <input>

@xiaoxiangmoe
Copy link
Collaborator

xiaoxiangmoe commented Sep 26, 2022

Can you provide me a demo repo?

@troy351
Copy link
Author

troy351 commented Sep 26, 2022

check this

@xiaoxiangmoe
Copy link
Collaborator

xiaoxiangmoe commented Sep 26, 2022

Use this fix temporarily:

// file: components.d.ts
declare module "element-ui/types/input" {
  interface ElInput {
    $props: {};
    $emit: {
      (event: "input", val: string, maybeAnother: number): void
    }
}
declare module "vue" {
  interface GlobalComponents {
    ElInput: typeof import("element-ui/types/input").ElInput;
  }
}
export {};

image

@xiaoxiangmoe xiaoxiangmoe added question Further information is requested upstream and removed need info labels Sep 26, 2022
@xiaoxiangmoe
Copy link
Collaborator

We can create a PR for element-ui types latter

@troy351
Copy link
Author

troy351 commented Sep 26, 2022

Thanks

@Papooch
Copy link

Papooch commented Mar 25, 2025

For late-comers like me, the asAny trick works perfectly well:

<el-input type="text" :value="item" @input="(val) => doSomething(index, asAny(val))" />
const asAny = (val: any) => val

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested upstream
Projects
None yet
Development

No branches or pull requests

5 participants