vuedx / languagetools

A collection of tools for better IDE experience.
https://vuedx.com
MIT License
1.26k stars 29 forks source link

[VueDX/TS(2322)] Input type="number" & type="checkbox" checks for string #172

Closed RobbinBaauw closed 2 years ago

RobbinBaauw commented 3 years ago

Describe the bug When using a <input type="number" />, and pass a number to v-model, you'll receive an error: Type 'string' is not assignable to type 'number'.. The same goes for checkboxes.

I assume this is because InputHTMLAttributes has the following type for value:

value?: string | string[] | number

However, MDN states that its value is a Number. Checkboxes are a bit more complicated, but boolean is not even part of the union found in Vue (so is probably also a bug in Vue).

This probably means some special cases should be handled for VueDX, or each type of input should have their own type definitions in Vue.

To Reproduce Steps to reproduce the behavior:

  1. Clone https://github.com/RobbinBaauw/vue-dx-172
  2. Run yarn dev to see that we indeed get numbers & booleans
  3. Run yarn build to see the build fail

Expected behavior Build does not fail.

Info (please complete the following information):

Additional context

Click to expand logs ``` src/App.vue:3:34 - error VueDX/TS2322: Type 'string' is not assignable to type 'number'. 1 | src/App.vue:4:36 - error VueDX/TS2322: Type 'boolean' is not assignable to type 'string | number | string[] | undefined'. 1 | 5 | node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts:588:4 589 | } 590 | 591 | export interface KeygenHTMLAttributes extends HTMLAttributes { | ~~~~~ 592 | autofocus?: boolean 593 | challenge?: string The expected type comes from property 'value' which is declared here on type 'ElementAttrs' src/App.vue:4:36 - error VueDX/TS2322: Type 'string' is not assignable to type 'boolean'. 1 | 5 | ```
znck commented 3 years ago

It supports v-model with number modifier. https://v3.vuejs.org/api/directives.html#v-model

Eitz commented 3 years ago

Any way to resolve the problem with checkbox+boolean type?

PePoDev commented 3 years ago

In my case

yarn create @vitejs/app my-vue-app --template vue-ts
cd my-vue-app
yarn install
yarn run build

and Voilà, It Error !

frck006 commented 3 years ago

@PePoDev : Not really, I have just create the project as you and here the log:

PS C:\Franck\cgid\vuejs3\vite> yarn create @vitejs/app vite02 --template vue-ts yarn create v1.22.10 [1/4] Resolving packages... [2/4] Fetching packages... info fsevents@1.2.11: The platform "win32" is incompatible with this module. info "fsevents@1.2.11" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... [4/4] Building fresh packages... success Installed "@vitejs/create-app@1.7.0" with binaries:

Done. Now run:

cd vite02 npm install (or yarn) npm run dev (or yarn dev)

Done in 10.72s. PS C:\Franck\cgid\vuejs3\vite> cd .\vite02\ PS C:\Franck\cgid\vuejs3\vite\vite02> yarn yarn install v1.22.10 warning package.json: No license field info No lockfile found. warning vite02@0.0.0: No license field [1/4] Resolving packages... [2/4] Fetching packages... info fsevents@2.3.2: The platform "win32" is incompatible with this module. info "fsevents@2.3.2" is an optional dependency and failed compatibility check. Excluding it from installation. [3/4] Linking dependencies... [4/4] Building fresh packages... success Saved lockfile. Done in 27.09s. PS C:\Franck\cgid\vuejs3\vite\vite02> yarn build yarn run v1.22.10 warning package.json: No license field $ vuedx-typecheck . && vite build Running for C:\Franck\cgid\vuejs3\vite\vite02 src/components/HelloWorld.vue:6:38 - error VueDX/TS2322: Type 'boolean' is not assignable to type 'string | number | string[] | undefined'. 3 | 4 | node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts:588:4 589 | } 590 | 591 | export interface KeygenHTMLAttributes extends HTMLAttributes { | ~ 592 | autofocus?: boolean 593 | challenge?: string The expected type comes from property 'value' which is declared here on type 'ElementAttrs'

src/components/HelloWorld.vue:6:38 - error VueDX/TS2322: Type 'string' is not assignable to type 'boolean'. 3 | 4 |

src/components/HelloWorld.vue:10:38 - error VueDX/TS2322: Type 'boolean' is not assignable to type 'string | number | string[] | undefined'. 7 | 8 | node_modules/@vue/runtime-dom/dist/runtime-dom.d.ts:588:4 589 | } 590 | 591 | export interface KeygenHTMLAttributes extends HTMLAttributes { | ~ 592 | autofocus?: boolean 593 | challenge?: string The expected type comes from property 'value' which is declared here on type 'ElementAttrs'

src/components/HelloWorld.vue:10:38 - error VueDX/TS2322: Type 'string' is not assignable to type 'boolean'. 7 | 8 |

error Command failed with exit code 2. info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

PePoDev commented 3 years ago

@frck006 I'm Following @znck comment

It supports v-model with number modifier. https://v3.vuejs.org/api/directives.html#v-model

Then I have fixed by change ref(true) -> ref("true") in HelloWorld.vue all line with type Boolean, And yes It work !. This is temporary solution and I don't have a better idea because I'm not good in Js and Ts. But this make me continue to setup pipeline.

frck006 commented 3 years ago

@PePoDev ; Thank you, it's ok.

rangermeier commented 3 years ago

Then I have fixed by change ref(true) -> ref("true") in HelloWorld.vue all line with type Boolean

This workaround doesn't work if you want to initialize with a false boolean: ref("false") will produce a truthy value, i.e. the checkbox would be checked.

Edit: To make this workaround work you need to set true-value="true" and false-value="false" on the input, see https://v3.vuejs.org/guide/forms.html#checkbox-2

frck006 commented 3 years ago

Cool, thank you @rangermeier

paultibbetts commented 3 years ago

I'm seeing two different messages for the same thing in the Vite vue-ts template.

The two v-models inside of HelloWorld.vue display:

Type 'string' is not assignable to type 'boolean'.VueDX/TS(2322)

as well as

Type 'boolean' is not assignable to type 'string | number | string[] | undefined'.VueDX/TS(2322) 
runtime-dom.d.ts(587, 3): The expected type comes from property 'value' which is declared here on type 'ElementAttrs<InputHTMLAttributes>`

which confirms @RobbinBaauw 's assumption about InputHTMLAttributes.

murphybob commented 3 years ago

Surprisingly this proved a blocker for me. My first thought was to try overriding the declaration of InputHTMLAttributes provided by runtime-dom.d.ts to add boolean to the union type for value, however unless I'm mistaken that is not possible in typescript. Even when I did this as a temporary hack in my local runtime-dom.d.ts it only fixed the warning on pssing the radio:value, but did not help with the v-model declaration.

I also couldn't find an equivalent of // @ts-ignore to use within the template 🤷 I've had to ditch the plugin for the time being but will keep a close eye on this issue as when it's working it's great! 👍

MartinMa commented 3 years ago

The Vue 3 documentation states that v-model on checkboxes and radio buttons uses the checked property and the change event instead of the value property and the input event compared to normal text inputs.

The value property serves a different purpose on checkboxes. The checked property is a boolean after all.

So when binding to v-model on an input of type checkbox (or radio) VueDX should probably perform a typecheck on the checked property and not on the value property (which seems to be happening now).

Looking at the code however it seems that this is already happening. Look at isCheckboxOrRadio. So I don't know where to look anymore? Or does this snippet only apply to JSX/TSX?

https://github.com/znck/vue-developer-experience/blob/084c05551c109ae8d2534b60b28a2e257135ad8c/packages/compiler-tsx/src/transforms/transformElement.ts#L362-L374

As a workaround:

Instead of this:

<input type="checkbox" v-model="isChecked" />

You can do this:

<input type="checkbox" :checked="isChecked" @change="onChange($event)" />
// Add this to your methods
onChange(event: Event): void {
  this.isChecked = (event?.target as HTMLInputElement).checked
}

Yes it is cumbersome.

AlansCodeLog commented 3 years ago

Is there no way to do something like // @ts-expect-error in templates? I can't find anything regarding this except that eslint comments supposedly work.

Also a shorter way to do this is to just lie.

For simple checkboxes you can get away with:

const value = ref<string>(false as any as string)

If you need to set the value a lot:

const FALSE = false as any as string
const TRUE = true as any as string
const value = ref<string>(FALSE)
value.value = TRUE
// if you need them in the template:
return {value, TRUE, FALSE}