Nuxt.js TypeScript集成 #

一、TypeScript配置 #

1.1 自动生成配置 #

Nuxt 自动生成 tsconfig.json,无需手动配置。

1.2 扩展配置 #

tsconfig.json

json
{
  "extends": "./.nuxt/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noUncheckedIndexedAccess": true
  }
}

1.3 类型检查 #

bash
npx nuxi typecheck

二、类型定义 #

2.1 全局类型 #

types/index.d.ts

typescript
interface User {
  id: number
  name: string
  email: string
  role: 'admin' | 'user' | 'guest'
  avatar?: string
  createdAt: string
  updatedAt: string
}

interface Post {
  id: number
  title: string
  content: string
  excerpt: string
  slug: string
  author: User
  tags: string[]
  status: 'draft' | 'published'
  publishedAt?: string
  createdAt: string
  updatedAt: string
}

2.2 API类型 #

types/api.d.ts

typescript
interface ApiResponse<T> {
  data: T
  message?: string
  pagination?: {
    page: number
    pageSize: number
    total: number
    totalPages: number
  }
}

interface ApiError {
  statusCode: number
  message: string
  errors?: Record<string, string[]>
}

type AsyncDataResult<T> = {
  data: Ref<T | null>
  pending: Ref<boolean>
  error: Ref<Error | null>
  refresh: () => Promise<void>
}

2.3 运行时配置类型 #

types/runtime-config.d.ts

typescript
declare module 'nuxt/schema' {
  interface RuntimeConfig {
    jwtSecret: string
    databaseUrl: string
    public: {
      apiBase: string
      siteName: string
      siteUrl: string
    }
  }
}

export {}

三、组件类型 #

3.1 Props类型 #

vue
<script setup lang="ts">
interface Props {
  title: string
  subtitle?: string
  count: number
  items: string[]
  config: {
    theme: 'light' | 'dark'
    size: 'small' | 'medium' | 'large'
  }
  onClose?: () => void
}

const props = withDefaults(defineProps<Props>(), {
  subtitle: '',
  count: 0,
  items: () => []
})
</script>

3.2 Emits类型 #

vue
<script setup lang="ts">
interface Emits {
  (e: 'update', value: string): void
  (e: 'delete', id: number): void
  (e: 'change', event: Event): void
}

const emit = defineEmits<Emits>()

const handleUpdate = () => {
  emit('update', 'new value')
}
</script>

3.3 插槽类型 #

vue
<script setup lang="ts">
interface SlotProps {
  item: Post
  index: number
  isSelected: boolean
}
</script>

<template>
  <div>
    <slot name="item" :item="item" :index="index" :isSelected="isSelected">
      {{ item.title }}
    </slot>
  </div>
</template>

3.4 泛型组件 #

vue
<script setup lang="ts" generic="T extends { id: number }">
interface Props {
  items: T[]
  selectedId?: number
}

const props = defineProps<Props>()

const selectedItem = computed(() => 
  props.items.find(item => item.id === props.selectedId)
)
</script>

四、组合式函数类型 #

4.1 返回类型 #

typescript
interface UseAuthReturn {
  user: Ref<User | null>
  token: Ref<string | null>
  isAuthenticated: ComputedRef<boolean>
  isLoading: ComputedRef<boolean>
  login: (credentials: LoginCredentials) => Promise<void>
  logout: () => Promise<void>
  fetchUser: () => Promise<void>
}

export const useAuth = (): UseAuthReturn => {
  const user = useState<User | null>('user', () => null)
  const token = useCookie('token')
  
  const isAuthenticated = computed(() => !!user.value && !!token.value)
  const isLoading = ref(false)
  
  const login = async (credentials: LoginCredentials) => {
    isLoading.value = true
    try {
      const { data } = await useFetch<LoginResponse>('/api/auth/login', {
        method: 'POST',
        body: credentials
      })
      
      if (data.value) {
        user.value = data.value.user
        token.value = data.value.token
      }
    } finally {
      isLoading.value = false
    }
  }
  
  const logout = async () => {
    await useFetch('/api/auth/logout')
    user.value = null
    token.value = null
  }
  
  const fetchUser = async () => {
    const { data } = await useFetch<User>('/api/auth/me')
    user.value = data.value
  }
  
  return {
    user,
    token,
    isAuthenticated,
    isLoading,
    login,
    logout,
    fetchUser
  }
}

4.2 选项类型 #

typescript
interface UseFetchOptions<T> {
  immediate?: boolean
  lazy?: boolean
  server?: boolean
  transform?: (data: T) => T
  default?: () => T
}

export const useApiFetch = <T>(
  url: string,
  options: UseFetchOptions<T> = {}
) => {
  const { immediate = true, lazy = false, server = true } = options
  
  return useFetch<T>(url, {
    immediate,
    lazy,
    server,
    ...options
  })
}

五、API类型 #

5.1 服务端类型 #

typescript
import { z } from 'zod'

const createPostSchema = z.object({
  title: z.string().min(1).max(200),
  content: z.string().min(1),
  tags: z.array(z.string()).optional(),
  status: z.enum(['draft', 'published']).default('draft')
})

type CreatePostInput = z.infer<typeof createPostSchema>

export default defineEventHandler(async (event) => {
  const body = await readValidatedBody(event, createPostSchema.parse)
  
  const post = await createPost(body)
  
  return { post }
})

5.2 响应类型 #

typescript
interface PostResponse extends ApiResponse<Post> {}

interface PostListResponse extends ApiResponse<Post[]> {
  pagination: {
    page: number
    pageSize: number
    total: number
    totalPages: number
  }
}

export default defineEventHandler(async (event): Promise<PostListResponse> => {
  const query = getQuery(event)
  
  const { posts, total } = await fetchPosts(query)
  
  return {
    data: posts,
    pagination: {
      page: Number(query.page) || 1,
      pageSize: Number(query.pageSize) || 10,
      total,
      totalPages: Math.ceil(total / (Number(query.pageSize) || 10))
    }
  }
})

六、类型工具 #

6.1 类型守卫 #

typescript
function isUser(value: unknown): value is User {
  return (
    typeof value === 'object' &&
    value !== null &&
    'id' in value &&
    'name' in value &&
    'email' in value
  )
}

const data: unknown = await fetchData()

if (isUser(data)) {
  console.log(data.name)
}

6.2 类型断言 #

typescript
const route = useRoute()

const id = route.params.id as string
const page = Number(route.query.page) as number

6.3 类型提取 #

typescript
type UserKeys = keyof User
type UserRole = User['role']
type OptionalUserFields = Partial<User>
type RequiredUserFields = Required<User>

七、严格模式 #

7.1 启用严格模式 #

tsconfig.json

json
{
  "extends": "./.nuxt/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUncheckedIndexedAccess": true
  }
}

7.2 处理可能为null的值 #

typescript
const user = useState<User | null>('user')

if (user.value) {
  console.log(user.value.name)
}

const name = user.value?.name ?? 'Guest'

八、类型导入 #

8.1 导入类型 #

typescript
import type { User, Post } from '~/types'

const user = ref<User | null>(null)
const posts = ref<Post[]>([])

8.2 自动导入类型 #

Nuxt 自动导入 types/ 目录下的类型。

九、最佳实践 #

9.1 类型优先 #

typescript
type Status = 'pending' | 'approved' | 'rejected'

interface Task {
  id: number
  title: string
  status: Status
}

const updateStatus = (task: Task, status: Status) => {
  task.status = status
}

9.2 避免any #

typescript
const data: any = fetchData()

const data: unknown = fetchData()

if (isExpectedType(data)) {
  process(data)
}

9.3 使用泛型 #

typescript
interface ApiResponse<T> {
  data: T
  message: string
}

const fetchUser = async (): Promise<ApiResponse<User>> => {
  return await $fetch('/api/user')
}

const fetchPosts = async (): Promise<ApiResponse<Post[]>> => {
  return await $fetch('/api/posts')
}

十、总结 #

本章介绍了 Nuxt.js TypeScript 集成:

  • TypeScript 配置
  • 类型定义
  • 组件类型
  • 组合式函数类型
  • API 类型
  • 类型工具
  • 严格模式
  • 最佳实践

TypeScript 可以显著提升代码质量和开发体验,下一章我们将学习测试策略。

最后更新:2026-03-28