vuejs / docs

📄 Documentation for Vue 3
https://vuejs.org
Other
2.82k stars 4.14k forks source link

Add TypeScript example for event handling #927

Closed TheDutchCoder closed 2 years ago

TheDutchCoder commented 3 years ago

Since a lot of developers will be dealing with event handling in their apps, it would be a good idea to add some documentation around this when doing event handling in combination with TypeScript.

To give an example, consider the following snippets:

<template>
  <input type="text" @change="handleChange" />
</template>

<script lang="ts">
import { defineComponent } from 'vue'

export default defineComponent({
  setup () {
    const handleChange = (evt) => { // `evt` will be of type `any`
      console.log(evt.target.value) // TS will throw an error here
    }

    return { handleChange }
  }
})
</script>

Here evt.target.value will throw a TypeScript error, because the event passed by the Vue template isn't typed. Strictly speaking this might not be Vue's job, since it's a native DOM event, but developers might wonder what's going on.

The solution, currently, is to cast the event target like so:

    const handleChange = (evt: Event) => {
      console.log((evt.target as HTMLInputElement).value)
    }

I think it would be beneficial to add that to the docs somewhere.

ITenthusiasm commented 3 years ago

Thanks for opening this! I'll pop back in soon!

ITenthusiasm commented 3 years ago

Okay cool. Just got a tiny bit of free time. I'm assuming docs-next is the Vue 3 docs, right? Would it make the most sense to add this change to this page? It seems like it shouldn't be too hard to do.

skirtles-code commented 3 years ago

@ITenthusiasm docs-next is indeed the correct repository for Vue 3 documentation. Contributions are always welcome but please make sure you've read the contributions guide before embarking on any changes.

At first glance I would have thought that the TypeScript Support page you linked would be the appropriate place to handle this, though I don't think it is specifically relevant to the reactive section. However, I don't use TypeScript myself so I don't feel entirely qualified to comment on exactly what should be documented where from that perspective.

ITenthusiasm commented 3 years ago

Got it. Thanks!

ITenthusiasm commented 3 years ago

Oh! And linking to the reactive section was an accident. :sweat_smile: I updated the comment to link to the TS Support page in general.

I didn't realize the docs were so smart that they update the URL while you scroll! :open_mouth:

Adrek commented 2 years ago

Is there some way to do inline from html ?. I say this to take advantage of the new way that Vue can v-model a prop.

<input type="text" :value="mypropparam" @input="$emit('update:mypropparam', $event.target.value)" />
// Lint error: The property 'value' doesn't exist in type EventTarget.
anburocky3 commented 2 years ago

Is there some way to do inline from html ?. I say this to take advantage of the new way that Vue can v-model a prop.

<input type="text" :value="mypropparam" @input="$emit('update:mypropparam', $event.target.value)" />
// Lint error: The property 'value' doesn't exist in type EventTarget.

Have you found a way to do it inline?

blocka commented 2 years ago

You can use the same casting syntax in the template:

($event.target as HTMLInputElement).value

however, I think vue should know the type of the event at this point, and this seems unnecessary.

giolf commented 1 year ago

You can use the same casting syntax in the template:

($event.target as HTMLInputElement).value

however, I think vue should know the type of the event at this point, and this seems unnecessary.

if i use as in the template i get: Unresolved variable or type as

AEROGU commented 1 year ago

Use the as to cast the target

function onInputChange(evt: Event) {
    let target = evt.target as HTMLInputElement;
    let imageFile = target.files?.item(0);
    if (imageFile) {
        processFile(imageFile);
    }
}

Or define an interface:

interface HTMLInputEvent extends Event {
    target: HTMLInputElement
}

// Then use the interface:
function onInputChange(evt: HTMLInputEvent) {
    let imageFile = evt.target.files?.item(0);
    if (imageFile) {
        processFile(imageFile);
    }
}
isimmons commented 1 year ago

How does this work?

@input or @change, Same TS error

interface HTMLInputEvent extends Event {
    target: HTMLInputElement
}
Type '(payload: HTMLInputEvent) => void' is not assignable to type '(payload: Event) => void'.
  Types of parameters 'payload' and 'payload' are incompatible.
const showAmount = (payload: HTMLInputEvent) => {
  console.log(payload.target?.value)
};

<input type="number" name="amount" :value="amount" @change="showAmount" />
emersonbottero commented 5 months ago

Hoe ugly is that!

 <input
      type="number"
      @change="
        (e) => {
          x = Number.parseInt((e.target as HTMLInputElement).value)
        }
      "
    />
mateja176 commented 1 month ago
const handleChange = ({ target }: Event) => {
  if (!(target instanceof HTMLInputElement)) {
    return
  }
  const { value } = target
  console.log(typeof value) // safe string
}