[SOLVED] How to dynamic defineEmits value in Vue3?

Issue

This Content is from Stack Overflow. Question asked by lizcodecode

As title,when I typed ’emit(update:${propsName}, evt);’ in CustomInput component,the error goes ‘Argument of type ‘update:${string}‘ is not assignable to parameter of type ‘”update:modelValue” | “update:lastName”‘.’

Any help would be much appreciated!

screen capture

(parameter) propsName: string

//App.js
<script setup lang="ts">
import { ref } from "vue";
import CustomInputVue from "./components/CustomInput.vue";

let firstName = ref("");
let lastName = ref("");

</script>
<template>
  <CustomInputVue
       v-model.no-whiteSpace="firstName"
       v-model:lastName="lastName"
  />
</template>
//CustomInput.vue
<script setup lang="ts">
const props = defineProps({
  modelValue: {
    type: String,
    required: false,
  },
  lastName: {
    type: String,
    required: false,
  },
  modelModifiers: {
    type: Object as any,
    required: false,
  },
});

const emit = defineEmits(["update:modelValue", "update:lastName"]);

const emitValue = (propsName: string, e: Event) => {
  let evt = (e.target as HTMLInputElement).value;
  if (props.modelModifiers["no-whiteSpace"]) {
    evt = evt.replace(/s/g, "");
  }
  emit(`update:${propsName}`, evt); =>
};
</script>

<template>
  <label class="block text-gray-700 text-sm font-bold mb-2" for="firstName">
    First Name
  </label>
  <input
    class="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none mb-4"
    id="firstName"
    type="text"
    placeholder="First Name"
    :value="modelValue"
    @input="emitValue('modelValue', $event)"
  />
  <label class="block text-gray-700 text-sm font-bold mb-2" for="lastName">
    Last Name
  </label>
  <input
    class="appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none"
    id="lastName"
    type="text"
    placeholder="Last Name"
    :value="lastName"
    @input="$emit('update:lastName', ($event.target as HTMLFormElement).value)"
  />
</template>

<style scoped></style>



Solution

The problem is that the update:${propsName} type is greater then "update:modelValue" | "update:lastName" (aka update:${"modelValue" | "lastName"}) you are allowed to use in emit
Reduce it down, and the problem will be solved

const emitValue = (propsName: "modelValue" | "lastName", e: Event) => {
  let evt = (e.target as HTMLInputElement).value;
  if (props.modelModifiers["no-whiteSpace"]) {
    evt = evt.replace(/\s/g, "");
  }
  emit(`update:${propsName}`, evt);
};

You are using Event and event.target, that is likely to be bad code.

const lastName = ref(props.lastName || "");
function emitName() {
  let newValue = lastName.value;
  // it's optional prop so "?."
  if (props.modelModifiers?.["no-whiteSpace"]) {
    newValue = newValue.replace(/\s/g, "");
  }
  emit("update:lastName", newValue)
}
const imputEl = ref<HTMLImputElement>() // if you need it
...
<imput v-model="lastName" ref="imputEl">

BTW consider using type-based syntax for props and emits, it compiles into same code internally

const props = defineProps<{
  modelValue?: string,
  lastName?: string,
  modelModifiers?: {'no-whiteSpace'?: boolean},
}>();
const emit = defineEmits<{
  (event: "update:modelValue" | "update:lastName", data: string): void;
}>();


This Question was asked in StackOverflow by lizcodecode and Answered by Dimava It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.

people found this article helpful. What about you?