How to type props in Vue with TypeScript
Typing props in Vue with TypeScript ensures components only receive valid data, provides IDE autocompletion for prop usage, and catches type errors at compile time rather than runtime.
As the creator of CoreUI with Vue and TypeScript development experience since 2014, I type every prop in CoreUI Vue components to prevent incorrect usage and generate accurate API documentation automatically.
There are two approaches: PropType with the Options API or defineProps generics with <script setup> — the latter is cleaner and more expressive for TypeScript projects.
Both produce the same runtime behavior; the difference is developer experience and type inference quality.
Type props using defineProps with a TypeScript interface in <script setup>.
<script setup lang="ts">
interface Address {
street: string
city: string
country: string
}
interface UserCardProps {
name: string
email: string
age?: number // Optional prop
role: 'admin' | 'user' | 'viewer' // Union type
address?: Address // Optional nested object
tags?: string[] // Optional array
onDelete?: () => void // Optional callback
}
const props = defineProps<UserCardProps>()
// props.name is string
// props.role is 'admin' | 'user' | 'viewer'
// props.address?.city is string | undefined
</script>
<template>
<div class="user-card">
<h3>{{ name }}</h3>
<p>{{ email }} — {{ role }}</p>
<p v-if="address">{{ address.city }}, {{ address.country }}</p>
<div>
<span v-for="tag in tags" :key="tag" class="badge">{{ tag }}</span>
</div>
</div>
</template>
TypeScript marks properties without ? as required. Calling <UserCard /> without the name, email, or role prop causes a TypeScript compile error.
Adding Default Values with withDefaults
<script setup lang="ts">
interface ButtonProps {
label: string
variant?: 'primary' | 'secondary' | 'danger'
size?: 'sm' | 'md' | 'lg'
disabled?: boolean
loading?: boolean
}
const props = withDefaults(defineProps<ButtonProps>(), {
variant: 'primary',
size: 'md',
disabled: false,
loading: false
})
</script>
withDefaults adds runtime default values while preserving TypeScript types. Without withDefaults, optional props are T | undefined — withDefaults removes undefined from the type for props with defaults.
PropType for Options API
Use PropType to type props when using the Options API.
import { defineComponent, PropType } from 'vue'
interface Product {
id: number
name: string
price: number
}
export default defineComponent({
props: {
product: {
type: Object as PropType<Product>,
required: true
},
quantity: {
type: Number,
default: 1
},
categories: {
type: Array as PropType<string[]>,
default: () => []
},
onAddToCart: {
type: Function as PropType<(product: Product) => void>
}
},
setup(props) {
// props.product is typed as Product
// props.categories is typed as string[]
console.log(props.product.name)
}
})
Object as PropType<Product> tells TypeScript the Object type validator corresponds to the Product interface. Without PropType, props.product would be typed as object, losing all property type information.
Best Practice Note
In CoreUI Vue components we exclusively use defineProps<Interface>() for <script setup> components because it gives better TypeScript inference than PropType without any extra typing. Export the props interface from the component file so parent components can import and reuse the type. See how to define props in Vue 3 with defineProps for the runtime validation aspects of defineProps.



