## Getting Started with Vue.js and TypeScript
Embarking on a Vue.js project with TypeScript unlocks a world of type safety, better IDE support, and fewer runtime errors. This guide walks you through a structured approach to building applications that are maintainable and performant. We'll begin with project initialization and progress to sophisticated component design, state management, and testing.
### Project Initialization Using Vite
The recommended way to kick off a new Vue.js + TypeScript project is with Vite, the lightning-fast build tool from the Vue team. Vite offers native ES modules, hot module replacement (HMR), and seamless TypeScript integration out of the box.
Run the following command to scaffold your project:
```bash
npm create vue@latest my-vue-ts-app -- --template vue-ts
```
This generates a starter with TypeScript pre-configured, including `vite.config.ts`, `tsconfig.json`, and `tsconfig.node.json` for optimal type checking. Key configurations include:
- **strict** mode enabled in `tsconfig.json` to catch potential issues early.
- `vue-tsc` for Vue-specific type checking during development.
Add `vue-tsc --noEmit` to your `package.json` scripts for type validation:
```json
{
"scripts": {
"type-check": "vue-tsc --noEmit"
}
}
```
This setup ensures your IDE (like VS Code with Volar extension) provides accurate IntelliSense. For production builds, Vite handles transpilation efficiently.
## Component Architecture
Vue 3's Composition API with `<script setup lang="ts">` is the gold standard for TypeScript components. It simplifies code while leveraging TypeScript's inference.
### Defining Typed Props
Use `defineProps` with a generic type for reactive, type-safe props:
```vue
<script setup lang="ts">
import { type PropType } from 'vue'
const props = defineProps({
items: {
type: Array as PropType<string[]>,
required: true
},
count: {
type: Number,
default: 0
}
})
</script>
```
Or with an interface for complex shapes:
```ts
interface User {
id: number
name: string
}
const props = defineProps<{
users: User[]
}>()
```
This prevents invalid prop assignments at compile time.
### Typed Emits
Emits should mirror real-world event contracts:
```vue
<script setup lang="ts">
const emit = defineEmits<{
(e: 'update', id: number): void
(e: 'delete', payload: { id: number; reason: string }): void
}>()
const handleClick = (id: number) => {
emit('update', id)
}
</script>
```
TypeScript ensures emit payloads match exactly, reducing bugs in parent-child communication.
### Template Refs with Types
Access DOM elements or child components safely:
```vue
<script setup lang="ts">
import { ref, type Ref } from 'vue'
const inputRef = ref<HTMLInputElement | null>(null)
const focusInput = () => {
inputRef.value?.focus()
}
</script>
<template>
<input ref="inputRef" />
</template>
```
For child components:
```ts
const childRef = ref<InstanceType<typeof ChildComponent> | null>(null)
```
## Composables: Reusable Logic
Composables are the heart of modular logic in Vue 3. Make them generic for maximum reusability.
Example: A typed `useCounter` composable:
```ts
// composables/useCounter.ts
import { ref, type Ref } from 'vue'
export function useCounter(initial: number = 0): {
count: Ref<number>
increment: () => void
decrement: () => void
} {
const count = ref(initial)
const increment = () => count.value++
const decrement = () => count.value--
return { count, increment, decrement }
}
```
Usage in component:
```vue
<script setup lang="ts">
const { count, increment } = useCounter(5)
</script>
```
For async operations, integrate with Suspense:
```ts
async function useFetch<T>(url: string): Promise<T> {
const data = ref<T | null>(null)
// fetch logic
return data.value as T
}
```
Libraries like [@vueuse/core](https://github.com/vueuse/vueuse) provide battle-tested composables with full TypeScript support—integrate them for utilities like `useStorage` or `useDebounce`.
## State Management with Pinia
Pinia is the official, type-friendly store solution. Define stores with `defineStore`:
```ts
// stores/user.ts
import { defineStore } from 'pinia'
import type { User } from '@/types'
export const useUserStore = defineStore('user', () => {
const users = ref<User[]>([])
const activeUser = ref<User | null>(null)
const addUser = (user: User) => users.value.push(user)
return { users, activeUser, addUser }
})
```
In components:
```vue
<script setup lang="ts">
const userStore = useUserStore()
</script>
```
Pinia's devtools integration shines with TypeScript, auto-generating action signatures.
## Routing with Vue Router
Typed routes enhance navigation safety. Use `RouteRecordRaw` for definitions:
```ts
// router/index.ts
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
const routes: RouteRecordRaw[] = [
{
path: '/users/:id',
component: () => import('@/views/User.vue'),
props: true
}
]
export const router = createRouter({
history: createWebHistory(),
routes
})
```
Typed params in components:
```vue
<script setup lang="ts">
const route = useRoute()
const id = route.params.id as string
</script>
```
## Testing Strategies
Vitest pairs perfectly with Vite for unit tests:
```bash
npm install -D vitest @vue/test-utils
```
Test a component:
```ts
// UserList.test.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import UserList from './UserList.vue'
describe('UserList', () => {
it('renders users', () => {
const wrapper = mount(UserList, {
props: { users: [{ id: 1, name: 'Alice' }] }
})
expect(wrapper.text()).toContain('Alice')
])
})
```
For E2E, Cypress or Playwright with TypeScript plugins ensure comprehensive coverage.
## Advanced Patterns and Tools
- **Volar**: Essential VS Code extension for Vue + TS ([Vue Language Tools](https://github.com/vuejs/language-tools)).
- **tsx**: For JSX/TSX support in Vue ([vuejs/tsx-support](https://github.com/vuejs/tsx-support)).
- **Iconify**: Typed icons with unplugin ([unplugin-icons](https://github.com/iconify-unplugin/unplugin-icons)).
- **Linting**: ESLint with `@vue/eslint-config-typescript` enforces consistent typing.
Real-world application: In a dashboard app, typed composables manage API fetches, Pinia handles global state, and strict props ensure UI consistency across teams.
## Performance and Deployment
Leverage Vite's analyzer for bundle insights. Use `vite-plugin-pwa` for progressive web apps. Deploy to Vercel or Netlify with zero-config TypeScript support.
By following these practices, your Vue.js + TypeScript apps will scale effortlessly, catching errors early and delighting developers with robust tooling.
<div style="text-align: center; margin-top: 2rem;">
<a href="https://cursor.directory/vuejs-typescript-best-practices" target="_blank" rel="noopener noreferrer" class="view-full-resource-btn" style="display: inline-block; background-color: #f97316; color: white; padding: 12px 24px; border-radius: 8px; text-decoration: none; font-weight: 600; transition: background-color 0.2s;">View Full Resource</a>
</div>