guide

TypeScript Best Practices for Vue Developers

Discover essential TypeScript patterns and best practices specifically tailored for Vue.js development to write more robust applications.

Mike TypeScript
4 min read
#typescript #vue #best-practices #development

TypeScript Best Practices for Vue Developers

TypeScript has become an essential tool for building robust Vue applications. Here are the best practices every Vue developer should know when working with TypeScript.

1. Define Component Props with Interfaces

Always use interfaces to define your component props:

interface UserProps {
  id: number
  name: string
  email: string
  isActive?: boolean
}

export default defineComponent({
  props: {
    user: {
      type: Object as PropType<UserProps>,
      required: true
    }
  }
})

With

<script setup>
:

interface Props {
  user: UserProps
  showDetails?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  showDetails: false
})

2. Type Your Composables

Make your composables type-safe:

interface UseApiReturn<T> {
  data: Ref<T | null>
  loading: Ref<boolean>
  error: Ref<string | null>
  fetch: () => Promise<void>
}

export function useApi<T>(url: string): UseApiReturn<T> {
  const data = ref<T | null>(null)
  const loading = ref(false)
  const error = ref<string | null>(null)

  const fetch = async () => {
    loading.value = true
    error.value = null
    
    try {
      const response = await $fetch<T>(url)
      data.value = response
    } catch (err) {
      error.value = err instanceof Error ? err.message : 'Unknown error'
    } finally {
      loading.value = false
    }
  }

  return {
    data,
    loading,
    error,
    fetch
  }
}

3. Use Generic Components

Create reusable generic components:

<script setup lang="ts" generic="T">
interface Props {
  items: T[]
  keyField: keyof T
  displayField: keyof T
}

const props = defineProps<Props>()
const emit = defineEmits<{
  select: [item: T]
}>()
</script>

<template>
  <ul>
    <li 
      v-for="item in items" 
      :key="String(item[keyField])"
      @click="emit('select', item)"
    >
      {{ item[displayField] }}
    </li>
  </ul>
</template>

4. Strict Type Checking Configuration

Configure TypeScript strictly in your

tsconfig.json
:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "exactOptionalPropertyTypes": true
  }
}

5. Type Your Store (Pinia)

If using Pinia, type your stores properly:

interface UserState {
  users: User[]
  currentUser: User | null
  loading: boolean
}

export const useUserStore = defineStore('user', {
  state: (): UserState => ({
    users: [],
    currentUser: null,
    loading: false
  }),
  
  getters: {
    activeUsers: (state): User[] => 
      state.users.filter(user => user.isActive)
  },
  
  actions: {
    async fetchUsers(): Promise<void> {
      this.loading = true
      try {
        this.users = await api.getUsers()
      } finally {
        this.loading = false
      }
    }
  }
})

Conclusion

TypeScript significantly improves the development experience and code quality in Vue applications. By following these best practices, you'll write more maintainable and error-free code.

Remember: start with basic typing and gradually add more sophisticated types as your application grows!

© 2026 Cristian Stalin Paredes Sánchez