TypeScript supports augmentation
TypeScript support in the Vue.js ecosystem has significantly improved in recent years, with enhanced type support across the core library and surrounding toolchain. Developers can now enjoy stricter type checking, smarter code hints, and a smoother development experience.
Deep Integration of TypeScript with Vue 3
Vue 3 was designed from the ground up with native TypeScript support in mind. Unlike Vue 2, which required decorators or class-component for type support, Vue 3's Composition API naturally lends itself to type inference. For example:
<script setup lang="ts">
import { ref } from 'vue'
// Automatically inferred as Ref<number>
const count = ref(0)
// Explicitly specifying the type
const user = ref<{ name: string; age: number }>({
name: 'Alice',
age: 25
})
function increment() {
count.value++ // Fully type-safe
}
</script>
This integration offers several key advantages:
- Automatic type checking for template expressions
- Runtime type validation for component props
- Better IDE autocompletion support
Type Definitions for Component Props
Vue 3 provides multiple ways to define component prop types:
import { defineComponent } from 'vue'
// Method 1: Runtime declaration
export default defineComponent({
props: {
title: String,
count: {
type: Number,
required: true
}
}
})
// Method 2: Pure TypeScript types
export default defineComponent({
props: {
title: {
type: String as PropType<'primary' | 'secondary'>,
default: 'primary'
},
items: {
type: Array as PropType<{ id: number; text: string }[]>,
required: true
}
}
})
// Method 3: Using generics
interface User {
name: string
age: number
}
export default defineComponent({
props: {
user: {
type: Object as PropType<User>,
required: true
}
}
})
Type Advantages of the Composition API
The Composition API is particularly well-suited for TypeScript due to its functional style, which better preserves type information:
import { ref, computed } from 'vue'
interface Todo {
id: number
text: string
completed: boolean
}
export function useTodo() {
const todos = ref<Todo[]>([])
const completedCount = computed(() => {
return todos.value.filter(todo => todo.completed).length
})
function addTodo(text: string) {
todos.value.push({
id: Date.now(),
text,
completed: false
})
}
return {
todos,
completedCount,
addTodo
}
}
Template Refs and Component Instance Types
When using refs in templates, you can precisely specify the ref type:
<template>
<ChildComponent ref="childRef" />
<input ref="inputRef" />
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import ChildComponent from './ChildComponent.vue'
const childRef = ref<InstanceType<typeof ChildComponent>>()
const inputRef = ref<HTMLInputElement>()
onMounted(() => {
// Fully type-safe method call
childRef.value?.someMethod()
// Type-safe DOM element access
inputRef.value?.focus()
})
</script>
Global Type Extensions
You can extend Vue's global type declarations to support custom options:
// types/vue.d.ts
import { ComponentCustomProperties } from 'vue'
declare module 'vue' {
interface ComponentCustomProperties {
$filters: {
formatDate: (date: Date) => string
}
}
}
// Usage
const app = createApp({})
app.config.globalProperties.$filters = {
formatDate: (date: Date) => date.toLocaleDateString()
}
// Usage in components
const formatted = vm.$filters.formatDate(new Date()) // Type-safe
Integration with Vue Router
Vue Router 4 provides full TypeScript support:
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/user/:id',
component: () => import('./views/User.vue'),
props: route => ({
id: Number(route.params.id), // Automatic type conversion
query: route.query.search
})
}
]
})
// Usage in components
import { useRoute } from 'vue-router'
const route = useRoute()
// route.params.id is correctly inferred as string | string[]
Type Support in Pinia State Management
Pinia, Vue's official state management library, offers excellent TypeScript support:
import { defineStore } from 'pinia'
interface UserState {
name: string
age: number
preferences: {
theme: 'light' | 'dark'
notifications: boolean
}
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
name: '',
age: 0,
preferences: {
theme: 'light',
notifications: true
}
}),
actions: {
updateName(newName: string) {
this.name = newName // Fully type-safe
},
toggleTheme() {
this.preferences.theme =
this.preferences.theme === 'light' ? 'dark' : 'light'
}
},
getters: {
isAdult: (state) => state.age >= 18
}
})
// Usage in components
const store = useUserStore()
store.updateName('Alice') // Type-checked
Toolchain Improvements
Modern Vue toolchains have also improved TypeScript support:
- Vite includes fast TypeScript compilation out of the box.
- Vitest provides a seamless type testing experience.
- Volar has replaced Vetur as the officially recommended IDE extension.
// Vitest test example
import { mount } from '@vue/test-utils'
import MyComponent from './MyComponent.vue'
describe('MyComponent', () => {
it('renders correctly', () => {
const wrapper = mount(MyComponent, {
props: {
// Autocompletion and type checking
title: 'Test',
count: 1
}
})
expect(wrapper.text()).toContain('Test')
})
})
Common Issues and Solutions
1. Missing Types in Third-Party Libraries
For libraries without type definitions, create declaration files:
// shims.d.ts
declare module 'untyped-library' {
export function doSomething(options: {
foo: string
bar?: number
}): Promise<void>
}
2. Complex Expressions in Templates
Use computed properties to maintain type safety with complex template expressions:
<template>
<div>{{ formattedDate }}</div>
</template>
<script setup lang="ts">
import { computed } from 'vue'
const props = defineProps<{
date: Date
format?: 'short' | 'long'
}>()
const formattedDate = computed(() => {
return props.format === 'short'
? props.date.toLocaleDateString()
: props.date.toLocaleString()
})
</script>
3. Handling Dynamic Components
Use markRaw
and type assertions for dynamic components:
import { markRaw } from 'vue'
import type { Component } from 'vue'
const components: Record<string, Component> = {
home: markRaw(defineAsyncComponent(() => import('./Home.vue'))),
about: markRaw(defineAsyncComponent(() => import('./About.vue')))
}
const currentComponent = ref<Component>(components.home)
Performance Optimization with Type Safety
TypeScript not only ensures type safety but also aids in performance optimization:
// Use const enums to reduce runtime code
const enum Routes {
HOME = '/',
ABOUT = '/about'
}
// Use literal types to restrict options
type Theme = 'light' | 'dark' | 'system'
interface Settings {
theme: Theme
animations: boolean
}
const settings = reactive<Settings>({
theme: 'light',
animations: true
})
Combining with JSX/TSX
Vue 3 supports writing components with TSX for better type support:
import { defineComponent } from 'vue'
interface ButtonProps {
type?: 'primary' | 'danger'
onClick?: (event: MouseEvent) => void
}
const Button = defineComponent({
setup(props: ButtonProps, { slots }) {
return () => (
<button
class={`btn-${props.type || 'default'}`}
onClick={props.onClick}
>
{slots.default?.()}
</button>
)
}
})
// Usage
<Button type="primary" onClick={(e) => console.log(e)}>
Click me
</Button>
Type-Safe Dependency Injection
Vue's provide/inject API also supports type safety:
import { inject, provide } from 'vue'
const ThemeSymbol = Symbol() as InjectionKey<'light' | 'dark'>
// Ancestor component
provide(ThemeSymbol, 'dark')
// Descendant component
const theme = inject(ThemeSymbol, 'light') // Default value 'light'
Advanced Type Patterns
Leverage TypeScript's advanced features to enhance Vue development:
// Conditional types
type ExtractComponentProps<T> = T extends new () => { $props: infer P }
? P
: never
// Mapped types
type OptionalProps<T> = {
[K in keyof T]?: T[K]
}
// Utility types
interface BaseProps {
id: string
class?: string
}
type WithDefaults<T, D> = Omit<T, keyof D> & {
[K in keyof D]: K extends keyof T ? T[K] : never
}
function defineDefaults<T, D>(props: T, defaults: D): WithDefaults<T, D> {
return { ...defaults, ...props } as any
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn