Deep integration of TypeScript
Deep Integration of TypeScript
The combination of TypeScript and Vue.js brings type safety and an improved development experience to modern frontend development. From basic configuration to advanced patterns, this integration can significantly enhance code quality and maintainability.
Basic Configuration
Integrating TypeScript into a Vue 3 project requires installing the necessary dependencies first:
npm install --save-dev typescript @vue/compiler-sfc
Then create the tsconfig.json
configuration file:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"strict": true,
"jsx": "preserve",
"moduleResolution": "node",
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"exclude": ["node_modules"]
}
Single-File Component Type Support
To use TypeScript in .vue
files, add the lang="ts"
attribute to the <script>
tag:
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
data() {
return {
count: 0 // Automatically inferred as number type
}
},
methods: {
increment(): void { // Explicitly specify return type
this.count++
}
}
})
</script>
Composition API Typing
The Composition API works exceptionally well with TypeScript:
import { ref, computed } from 'vue'
interface User {
id: number
name: string
email: string
}
export function useUser() {
const user = ref<User | null>(null)
const userName = computed(() => {
return user.value?.name || 'Guest'
})
function setUser(newUser: User) {
user.value = newUser
}
return {
user,
userName,
setUser
}
}
Props Type Definitions
Provide precise type definitions for component Props:
<script lang="ts">
import { defineComponent, PropType } from 'vue'
interface Book {
title: string
author: string
year: number
}
export default defineComponent({
props: {
book: {
type: Object as PropType<Book>,
required: true
},
rating: {
type: Number,
validator: (value: number) => {
return value >= 0 && value <= 5
}
}
}
})
</script>
Custom Type Declarations
Extend global properties and component options for Vue:
// src/types/vue.d.ts
import { ComponentCustomProperties } from 'vue'
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$filters: {
formatCurrency(value: number): string
}
}
}
Integration with Vuex/Pinia
Using TypeScript with Pinia:
// stores/user.ts
import { defineStore } from 'pinia'
interface UserState {
users: User[]
currentUser: User | null
}
export const useUserStore = defineStore('user', {
state: (): UserState => ({
users: [],
currentUser: null
}),
actions: {
async fetchUsers() {
const response = await fetch('/api/users')
this.users = await response.json()
}
}
})
Type-Safe Routing
Add type support for Vue Router:
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: () => import('../views/Home.vue'),
meta: {
requiresAuth: true
}
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
// Extend route meta field types
declare module 'vue-router' {
interface RouteMeta {
requiresAuth?: boolean
roles?: string[]
}
}
Advanced Type Patterns
Leverage TypeScript's advanced features to enhance Vue development:
// Conditional rendering type guard
function isModalComponent(
component: unknown
): component is { show: () => void; hide: () => void } {
return (
typeof component === 'object' &&
component !== null &&
'show' in component &&
'hide' in component
)
}
// Usage in components
const modal = ref<InstanceType<typeof ModalComponent> | null>(null)
onMounted(() => {
if (modal.value && isModalComponent(modal.value)) {
modal.value.show()
}
})
Type Applications in Testing
Add types to Vue component tests:
import { mount } from '@vue/test-utils'
import Counter from '@/components/Counter.vue'
describe('Counter.vue', () => {
it('increments count when button is clicked', async () => {
const wrapper = mount(Counter)
const button = wrapper.find<HTMLButtonElement>('button')
await button.trigger('click')
expect(wrapper.vm.count).toBe(1)
expect(button.element.textContent).toContain('1')
})
})
Performance Optimization Considerations
Use types to avoid unnecessary runtime checks:
// Use enums instead of string constants
enum LoadingState {
IDLE = 'idle',
PENDING = 'pending',
SUCCESS = 'success',
ERROR = 'error'
}
const state = ref<LoadingState>(LoadingState.IDLE)
// Use literal types to limit options
type AlertType = 'success' | 'warning' | 'error' | 'info'
function showAlert(type: AlertType, message: string) {
// ...
}
Third-Party Library Integration
Create declaration files for libraries without type definitions:
// src/types/legacy-plugin.d.ts
declare module 'legacy-plugin' {
interface PluginOptions {
debug?: boolean
maxRetries?: number
}
export function init(options: PluginOptions): void
export function doSomething(input: string): Promise<number>
}
Type Utility Helpers
Create reusable type utilities:
// src/utils/types.ts
type UnwrapRef<T> = T extends Ref<infer U> ? U : T
type MaybeRef<T> = T | Ref<T>
function useDouble<T extends number>(value: MaybeRef<T>): ComputedRef<UnwrapRef<T>> {
const resolved = isRef(value) ? value : ref(value)
return computed(() => resolved.value * 2)
}
Type Checking in Templates
Get type checking within templates via the Volar extension:
// tsconfig.json
{
"vueCompilerOptions": {
"target": 3,
"experimentalTemplateMacros": true,
"strictTemplates": true
}
}
Type-Driven Development
Design components starting from type definitions:
// Define types first
interface PaginationProps {
current: number
total: number
pageSize?: number
showQuickJumper?: boolean
onChange?: (page: number) => void
}
// Then implement the component
const Pagination = defineComponent({
props: {
current: { type: Number, required: true },
total: { type: Number, required: true },
pageSize: { type: Number, default: 10 },
showQuickJumper: { type: Boolean, default: false },
onChange: { type: Function as PropType<(page: number) => void> }
}
// ...
})
Complex State Management
Use typed state machines to manage complex states:
type State =
| { status: 'idle' }
| { status: 'loading'; requestId: string }
| { status: 'success'; data: any }
| { status: 'error'; error: Error }
const state = ref<State>({ status: 'idle' })
function startLoading() {
if (state.value.status !== 'idle') return
state.value = {
status: 'loading',
requestId: generateId()
}
}
Type-Safe Dependency Injection
Ensure type safety when using provide/inject:
// src/providers/auth.ts
import { inject, InjectionKey, provide } from 'vue'
interface AuthContext {
user: Ref<User | null>
login: (credentials: { email: string; password: string }) => Promise<void>
logout: () => void
}
const authInjectionKey: InjectionKey<AuthContext> = Symbol('auth')
export function provideAuth(context: AuthContext) {
provide(authInjectionKey, context)
}
export function useAuth() {
const context = inject(authInjectionKey)
if (!context) {
throw new Error('useAuth must be used within an AuthProvider')
}
return context
}
Type and Documentation Generation
Generate API documentation using type annotations:
/**
* Format a date
* @param date - The date to format, can be a Date object or timestamp
* @param format - The format string, e.g. 'YYYY-MM-DD'
* @returns The formatted date string
*/
function formatDate(date: Date | number, format: string): string {
// ...
}
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn