Skip to the solution
EasyFrontend9 min read

How do defineProps and defineEmits work in <script setup>?

684 views

The problem

Question presented to candidate: "In our <script setup> components I see defineProps and defineEmits used without any import. Where do they come from, what do they turn into, and what are the rules and limits when you use them with TypeScript?"

What a strong answer should cover:

  • They are compiler macros: the SFC compiler removes the calls and writes props and emits options into the component; there is nothing to import (importing triggers a warning).
  • defineProps returns the props object; defineEmits returns a typed emit function.
  • Two forms: runtime (defineProps({ title: String })) and type-based (defineProps<{ title: string }>()), which the compiler converts into runtime options. You cannot use both in one call.
  • Defaults for type-based props: withDefaults(), or since Vue 3.5 reactive props destructure (const { likes = 0 } = defineProps<...>()), with the getter rule for watch.
  • The arguments are hoisted out of setup(), so they cannot reference local variables.
  • vue-tsc checks the parent's usage against these types.

Clarifying questions expected:

  • "Is the project on Vue 3.5 or later? That decides between withDefaults and destructured defaults."

Code / implementation expected: Yes. A typed component using both macros, ideally with the compiled output.

  • script-setup
  • props

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read Β· 9 sections

Read the code

VueTyped defineProps with destructured defaults and typed defineEmits (two files)
<!-- LikeButton.vue -->
<script setup lang="ts">
import { watch } from "vue";

// Type-based declarations: the compiler turns these types into runtime options
const { title, likes = 0 } = defineProps<{
  title: string;
  likes?: number;
}>();

const emit = defineEmits<{
  like: [count: number];
  reset: [];
}>();

// Destructured props stay reactive in Vue 3.5+, but watch needs a getter
watch(() => likes, (now, before) => console.log(`  [child] likes prop changed ${before} -> ${now}`));
</script>

<template>
  <button class="like" @click="emit('like', likes + 1)">{{ title }}: {{ likes }}</button>
  <button class="reset" @click="emit('reset')">reset</button>
</template>

<!-- App.vue -->
<script setup lang="ts">
import { ref } from "vue";
import LikeButton from "./LikeButton.vue";
const count = ref(0);
</script>

<template>
  <LikeButton title="Vue" :likes="count" @like="(n) => (count = n)" @reset="count = 0" />
  <LikeButton title="Defaults only" />
</template>
Real compileScript() output for withDefaults + type-based defineEmits
// What @vue/compiler-sfc compileScript() produced for
//   const props = withDefaults(defineProps<{ title: string; likes?: number; tags?: string[] }>(), {
//     likes: 0,
//     tags: () => [],
//   })
//   const emit = defineEmits<{ like: [count: number]; close: [] }>()
import { defineComponent as _defineComponent } from 'vue'
export default /*@__PURE__*/_defineComponent({
  props: {
    title: { type: String, required: true },
    likes: { type: Number, required: false, default: 0 },
    tags: { type: Array, required: false, default: () => [] }
  },
  emits: ["like", "close"],
  setup(__props: any, { expose: __expose, emit: __emit }) {
  __expose();
const props = __props
const emit = __emit
const __returned__ = { props, emit }
Object.defineProperty(__returned__, '__isScriptSetup', { enumerable: false, value: true })
return __returned__
}
})

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 11 of 92 in Vue.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Vue.js questions
Up nextEasy

How do you create a new Vue 3 app, and what is in the project structure?

Continue