language-tools: Detect unknown/undefined prop usage

This is a proposal for enhancing vue-tsc

Example:

<MyComponent
    :propThatDoesNotExist="42"
/>

When running npx vue-tsc --noEmit i want it to detect that propThatDoesNotExist is not presently defined in myComponent. This could produce a warning or error.

Feature like this currently exists in TS files. When trying to edit fields that is not defined in some typescript class it will output: Property ‘xxx’ does not exist on type ‘MyClass’.

This would be very helpful for Vue developers when renaming props or performs refactoring or git rebases as looking through the code manually can be tedious.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 4
  • Comments: 45 (16 by maintainers)

Commits related to this issue

Most upvoted comments

In 0.36, custom / unknown attrs types behavior handle responsibility will transfer to developer.

  • Expand custom attrs example
<template>
    <div myprop="a">
</template>
// env.d.ts
declare module '@vue/runtime-dom' {
    interface HTMLAttributes {
        myprop: string
    }
}

export { }
  • Allow any attrs
// env.d.ts

// for native html elements
declare module '@vue/runtime-dom' {
    interface HTMLAttributes {
        [key: string]: any
    }
    interface SVGAttributes {
        [key: string]: any
    }
}

// for vue components
declare module '@vue/runtime-core' {
    interface AllowedComponentProps {
        [key: string]: any
    }
}

export { }

This isn’t just about fallthrough either, as shown in https://github.com/johnsoncodehk/volar/issues/1383 this is causing errors for many completely standard HTML attributes directly on the elements. It seems unreasonable requiring people to add custom declaration files to avoid that

Until this is fixed, the proper solution is to revert this feature or hide it behind a flag and release a new version because in the current state it’s just plane broken

I agree that this type of strict linting should be opt-in, fallthrough attributes are a common use case with Vue and the default configuration of Volar should just work with the examples from the Vue docs.

Can you make this disabled by default instead?

I still don’t understand what the real point is, as this change breaks totally valid code all over the place for known attributes like all aria attributes.

I’m sure there’s good intentions here, but people (especially new users) will have no idea what is going on when their data- attributes are throwing errors suddenly.

@lohchab I can’t make it work on components image

LIke on native components it works fine, but what about component attributes? I have VInput component which is just a wrapper around native input. SO i have to describe ALL native attributes in props to apply them?

I got you. Yes, you are right I can confirm its is not working. Its only working for HTML Elements.

For a temporary workaround till the time there’s a no solution to this my recommendation is that you switch your Volar Extension Version to the ‘0.35.2’ and Restart the Visual Studio Code.

image

This change is bonkers! How do volar check where this attribute that I passed on purpose ends up? Does it just “assumes” it is the root element of the template? But even that doesn’t work properly. Vue has an attribute fallthrough on purpose and this change makes vue-tsc crash on basically every component where I use attribute fallthrough (which I use A LOT).

This feature should be opt-in and not opt-out.

How can I disable this?

Is it possible to make an option like mode=“strict” to change default behavior? I mean, props existence check looks like an edge case than something default. Because everything that not described in props are custom attributes, that’s vue default behavior

As @TheDutchCoder said, data-* and aria-* are not unknown attributes, specially on “plain” html elements. They are standard W3C recommendations. The following piece of code is valid html and it should not throw an error regardless of the setting:

<template>
    <input aria-describedby="emailHelp">
    <p id="emailHelp">aria help...</p> 
</template>

Fix by ba2835fdcf233cad92bdd39519c0d723b7dee42e.

In v0.36.1 this behavior is control by vueCompilerOptions.experimentalSuppressUnknownJsxPropertyErrors and default enabled, if you want report unknown props you need config:

// tsconfig.json
{
  "vueCompilerOptions": {
    "experimentalSuppressUnknownJsxPropertyErrors": false
  }
}

If you see how volar hacking JSX types to ignore unknown prop errors, you may want to disable it. (This shouldn’t be part of volar’s control)

https://github.com/johnsoncodehk/volar/blob/104af79387fec40ec3c9e82299f1258f1947673e/packages/vue-typescript/src/typescriptRuntime.ts#L282-L290

I can confirm that the update fixes all issues. The feature is now opt-in as described above. Thank you for the quick fix!

Okay I’ll give it a spin later.

Just to clarify: data- and aria- attributes are not unknown jsx attributes/properties.

So regardless of the setting, these should never throw errors, which was the original issue.

Thanks to this feature, I noticed that the Vuetify property names had been updated and was able to correct them. 😃 However, it took some work to resolve the issue. FYI:

  • I had to explicitly add @vue/runtime-core and @vue/runtime-dom to dependencies (probably because I use pnpm).
  • The .d.ts file must be separate from the file defining the vite’s ImportMetaEnv (if any).

For now, I use the following definition in env2.d.ts.

import '@vue/runtime-core';
import '@vue/runtime-dom';

// for vue components
declare module '@vue/runtime-core' {
  export interface AllowedComponentProps {
    [key: string]: any;
  }
}

// for native html elements
declare module '@vue/runtime-dom' {
  export interface HTMLAttributes {
    // allow any data-* attr
    [key: `data${string}`]: string;
  }
}

export {};

Complex libraries such as Vuetify do not always have all properties defined (especially if they are passed through), and users will end up having to disable the feature completely. The ideal solution would be for the type definitions of Vuetify components to be updated, but this is not always done quickly. It seems difficult to use without at least a way to define additional attributes for individual components.

FWIW, the fix outlined in the comment above related to updating the env.d.ts file is not working in my team’s project. Unsure of the exact reasons, but one potential difference is our team is still using Vue 2 /w the composition API, so declaring types on @vue/runtime-core (and potentially even @vue/runtime-dom) may not be working as expected.

Still trying to troubleshoot and find a more concrete reason why it’s not working for us, but figured I’d share that in case other Vue 2 users are finding the fix is not working for them too.

EDIT: Has me wondering if it would be possible to add a feature to Volar where it uses the project’s version of vue-tsc instead of the built-in version - that way these breaking changes can be opted into by updating your project’s dependencies.

I assume the good intent. As people mentioned, standard html attributes throw errors. So I wonder why DX is being traded for being strict on unknown props?

What is the point of making it strict and then advising people to allow any attribute? Why not allowing any html attributes by default rather than expecting people to update their env.d.ts files?

Let’s imagine data-cy attribute used for testing, is it treated as HTMLAttributes or AllowedComponentProps?

Imo this feature would be only useful if volar could analyze which element receives the attributes in the end. And only then, it can perform a check if that attribute is valid on that element. However, this is almost impossible to analyze statically because you can change where your attrs end up as you want.

It doesn’t make sense to me to restrict passing attributes to a component because that basically takes away a lot free customizability (look at all attributes you can give to inputs for validation reasons. You don’t want that as a prop).

So i can’t even make pure component wrapper to my button without adding env.d.ts 😄 Because this component don’t have props at all

image

image

// env.d.ts

// for native html elements
declare module '@vue/runtime-dom' {
    interface HTMLAttributes {
        [key: string]: any
    }
}

// for vue components
declare module '@vue/runtime-core' {
    interface AllowedComponentProps {
        [key: string]: any
    }
}

export { }

Thankyou @johnsoncodehk I can confirm adding this code to env.d.ts file has solved the unknown property problems for both HTML and Vue Component Elements. Keep up the good work. We all appreciate what you are doing on Volar!

As @TheDutchCoder said, data-* and aria-* are not unknown attributes, specially on “plain” html elements. They are standard W3C recommendations. The following piece of code is valid html and it should not throw an error regardless of the setting:

<template>
    <input aria-describedby="emailHelp">
    <p id="emailHelp">aria help...</p> 
</template>

In 0.36, custom / unknown attrs types behavior handle responsibility will transfer to developer.

  • Expand custom attrs example
<template>
    <div myprop="a">
</template>
// env.d.ts
declare module '@vue/runtime-dom' {
    interface HTMLAttributes {
        myprop: string
    }
}

export { }
  • Allow any attrs
// env.d.ts

// for native html elements
declare module '@vue/runtime-dom' {
    interface HTMLAttributes {
        [key: string]: any
    }
}

// for vue components
declare module '@vue/runtime-core' {
    interface AllowedComponentProps {
        [key: string]: any
    }
}

export { }

Not so great… like build-in vue directive, it should not throw an error, such as v-show etc… right?

@lohchab I can’t make it work on components image

LIke on native elements it works fine, but what about component attributes? I have VInput component which is just a wrapper around native input. SO i have to describe ALL native attributes in props to apply them?

You should use AllowedComponentProps interface, please check updated example code.

exactly. it might work for html elements but attribute fallthrough for components doesn’t work at all

@lohchab I can’t make it work on components image

LIke on native elements it works fine, but what about component attributes? I have VInput component which is just a wrapper around native input. SO i have to describe ALL native attributes in props to apply them?

I also think it is great to have this feature. IMO data-* and aria-* can be ignored even if it is not used.

This is intentional behavior as this is often used in css selectors.

I’ll explore add a option in vueCompilerOptions to change this behavior.