Skip to content

TypeScript 支持

Nuxt 提供零配置的 TypeScript 支持,无需学习 TypeScript 就能享受类型安全。

为什么 Nuxt 对 TypeScript 支持这么好?

不用 TypeScript用 TypeScript
useFetch 返回值类型是 any,不知道有哪些字段useFetch 返回值有精确类型,IDE 自动补全
调用 API 时传错了参数类型不会报错传错了类型立即红线提示
重构代码时怕改漏改了类型定义,所有引用处都报错
看别人代码不知道函数参数是什么鼠标悬停就能看到类型

初学者建议

如果你还不熟悉 TypeScript,可以先用"宽松模式"——在 .vue 文件中用 <script setup lang="ts">,但不写类型注解。TypeScript 会自动推断大部分类型。等你熟悉后再逐步添加类型。

零配置 TypeScript

Nuxt 项目开箱即用 TypeScript:

  • 自动生成 tsconfig.json
  • 自动生成类型声明
  • .vue 文件支持 <script setup lang="ts">
  • 无需手动配置 vue-tsc
ts
// nuxt.config.ts — 只需要这一行
export default defineNuxtConfig({
  compatibilityDate: '2025-07-15',
})

运行 nuxt prepare 自动生成 .nuxt/tsconfig.json

json
// tsconfig.json(你只需要写这一行)
{
  "extends": "./.nuxt/tsconfig.json"
}

你不需要了解 tsconfig.json 的细节

Nuxt 自动管理它。你只需要知道:类型提示不正常时,运行 nuxt prepare

类型自动生成

Nuxt 自动为以下内容生成类型:

1. API 路由类型

ts
// server/api/users/index.get.ts
export default defineEventHandler(() => {
  return [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
})

在客户端使用时自动推断响应类型:

vue
<script setup lang="ts">
// data 类型自动推断为 { id: number; name: string }[]
// 不需要手动指定泛型!
const { data } = await useFetch('/api/users')
</script>

这是怎么做到的?

Nuxt 扫描 server/api/ 下的文件,分析返回值类型,自动生成类型声明。当你在客户端使用 useFetch('/api/users') 时,TypeScript 知道返回的数据结构。

INFO

前提:你的 API 路由必须有明确的返回值 不能是 any。如果返回类型无法推断(如调用了外部 API),你需要手动指定泛型

2. 组件 Props 类型

vue
<!-- app/components/UserCard.vue -->
<script setup lang="ts">
defineProps<{
  name: string
  age: number
  email?: string  // 可选属性
}>()
</script>

defineProps<{...}>() 是 Vue 3 的类型式 Props 声明

比对象式声明更简洁:

ts
// 对象式(不推荐)
defineProps({
name: { type: String, required: true },
age: { type: Number, required: true },
email: { type: String, required: false },
})

// 类型式(推荐)
defineProps<{
name: string
age: number
email?: string
}>()

3. 自动导入类型

app/components/app/composables/app/utils/ 的类型自动生成在:

  • .nuxt/components.d.ts
  • .nuxt/imports.d.ts

Nuxt 4 的 TypeScript 改进

Nuxt 4 将 TypeScript 项目分为三个独立上下文:

text
项目根/
├── app/              → .nuxt/tsconfig.app.json
├── server/           → .nuxt/tsconfig.server.json
├── shared/           → 两边都可以用
└── nuxt.config.ts    → .nuxt/tsconfig.nuxt.json

好处:

  • 更准确的类型:前端代码不会看到服务端类型,反之亦然
  • 更少的错误:避免在错误上下文中使用 API
  • 更快的 IDE:TypeScript 检查范围更小

实际效果

ts
// 在 app/pages/index.vue 中
readBody()   // ❌ TypeScript 报错:这是服务端 API,客户端不可用
useState()   // ✅ 正确:客户端可用

// 在 server/api/users.ts 中
useState()   // ❌ 可能报错:这是客户端 API,服务端不推荐用
readBody()   // ✅ 正确:服务端可用

Nuxt 4 之前,这种错误只有在运行时才会发现。现在 TypeScript 在编码阶段就能提示。

严格模式

ts
// nuxt.config.ts
export default defineNuxtConfig({
  typescript: {
    strict: true,
    typeCheck: true,  // 构建时进行类型检查
  },
})

要不要开严格模式?

情况建议
刚开始学 Nuxt不开,先把功能做出来
有 TypeScript 基础开,类型安全更可靠
团队协作必须开,避免低级错误

typeCheck: true 会让构建变慢,因为每次构建都要做类型检查。开发时可以关闭,CI/CD 中开启。

常见严格检查问题

ts
// ❌ 隐式 any——函数参数没有类型
function greet(name) { }

// ✅ 显式类型
function greet(name: string) { }
ts
// ❌ 可能为 undefined——data.value 可能为 null
const user = data.value.name

// ✅ 安全访问
const name = data.value?.name ?? 'Unknown'

类型声明文件

全局类型扩展

ts
// app/types/index.d.ts
// 扩展 NuxtApp,让插件提供的方法有类型提示
declare module '#app' {
  interface NuxtApp {
    $myPlugin: { doSomething: () => void }
  }
}

// 扩展 runtimeConfig,让配置项有类型提示
declare module 'nuxt/schema' {
  interface RuntimeConfig {
    apiSecret: string
  }
  interface PublicRuntimeConfig {
    apiBase: string
  }
}

export {}  // 必须有这个,让文件成为模块

为什么需要 export {}

TypeScript 区分"脚本"和"模块"。没有 exportimport 的文件是脚本,其中的 declare 会影响全局。加上 export {} 让文件成为模块,declare module 才能正确扩展指定模块而不是污染全局。

为 API 响应定义类型

ts
// shared/types/api.ts
export interface ApiResponse<T> {
  code: number
  message: string
  data: T
}

export interface User {
  id: number
  name: string
  email: string
}

export type UserResponse = ApiResponse<User>
vue
<script setup lang="ts">
import type { UserResponse } from '~~/shared/types/api'

const { data } = await useFetch<UserResponse>('/api/user/1')
// data.value.data 的类型是 User,有完整的类型提示
</script>

最佳实践

shared/types/ 中定义所有 API 相关类型。服务端和客户端都能使用,确保类型一致。

类型检查命令

bash
# 运行类型检查
npx nuxt typecheck

# 或添加到 package.json scripts
# "typecheck": "nuxt typecheck"

推荐在 CI/CD 中加入类型检查

确保每次提交都不会引入类型错误:

yaml
# .github/workflows/ci.yml
- run: npm run typecheck

在 Vue 组件中使用 TypeScript

vue
<script setup lang="ts">
// Props 类型
const props = defineProps<{
  title: string
  count?: number
}>()

// Emits 类型
const emit = defineEmits<{
  update: [value: string]   // 事件名: [参数类型]
  delete: [id: number]
}>()

// Ref 类型
const name = ref<string>('')
const items = ref<string[]>([])

// 计算属性自动推断类型
const fullName = computed(() => `${firstName.value} ${lastName.value}`)

// 异步函数
async function fetchData() {
  const data = await $fetch<User>('/api/user/1')
  // data 类型为 User
}
</script>

defineEmits 的新语法

ts
// 旧的调用签名语法(不推荐)
defineEmits<{
(e: 'update', value: string): void
(e: 'delete', id: number): void
}>()

// 新的元组语法(推荐,更简洁)
defineEmits<{
update: [value: string]
delete: [id: number]
}>()

常见问题

类型找不到

bash
# 重新生成类型
npx nuxt prepare

什么时候需要运行 nuxt prepare

  • 新增了组件/composable 但 IDE 不识别
  • Git 切换分支后
  • npm install 后(通常自动运行)
  • 类型提示突然消失

第三方模块类型缺失

ts
// env.d.ts
declare module 'some-untyped-lib' {
  const lib: any
  export default lib
}

为什么有些库没有类型?

不是所有 npm 包都提供了 TypeScript 类型声明。对于这些库,你需要手动声明类型,或者用 any 作为临时方案。

严格模式下的常见错误

  1. useRoute().params 类型不精确:使用 getRouteParams 或手动断言
    ts
    // ❌ params.id 是 string | string[]
    const id = route.params.id
    
    // ✅ 手动断言
    const id = route.params.id as string

2. **`useFetch` 返回类型推断失败**:显式指定泛型
   ```ts
   // ❌ 有时推断不出类型
   const { data } = await useFetch('/api/users')

   // ✅ 手动指定泛型
   const { data } = await useFetch<User[]>('/api/users')
  1. 组件 Props 类型推导失败:使用 defineProps<{...}>() 而不是对象语法

.vue 文件中 import 路径报错

确保 tsconfig.json 继承了 .nuxt/tsconfig.json,并且运行过 nuxt prepare

知识脉络

text
服务引擎 Nitro → 你在这里:TypeScript 支持

                   ├─→ 相关:shared/ 目录(类型定义放哪里)

                   └─→ 相关:测试与调试(19章)

基于 Nuxt 4 官方文档整理编写