---
title: "字段类型化"
description: "通过 TypeScript 模块扩展为宽事件添加编译时类型安全。防止拼写错误，并确保代码库中字段名称的一致性。"
canonical_url: "https://evlog.zhcndoc.com/learn/typed-fields"
---
# 字段类型化

> 通过 TypeScript 模块扩展为宽事件添加编译时类型安全。防止拼写错误，并确保代码库中字段名称的一致性。

默认情况下，`useLogger` 接受任意字段，这对快速入门非常有用。但随着代码库的扩大，不一致的问题会逐渐显现：一条路由记录 `user`，另一条记录 `account`，第三条记录 `userId`。字段类型化通过可选的编译时类型安全机制来解决这个问题。

<typed-fields-intellisense>



</typed-fields-intellisense>

## 基本用法

为你的字段定义一个接口，并将其作为泛型传递给 `useLogger`：

```typescript [server/api/checkout.post.ts]
import { useLogger } from 'evlog'

interface CheckoutFields {
  user: { id: string; plan: string }
  cart: { items: number; total: number }
  action: string
}

export default defineEventHandler(async (event) => {
  const log = useLogger<CheckoutFields>(event)

  log.set({ user: { id: '123', plan: 'pro' } })  // 正确
  log.set({ cart: { items: 3, total: 9999 } })    // 正确
  log.set({ action: 'checkout' })                  // 正确

  log.set({ account: '...' })                      // TypeScript 错误
  log.set({ usr: { id: '123' } })                  // TypeScript 错误

  return { success: true }
})
```

TypeScript 随后会在编译时捕获拼写错误或未知字段，而这是字段名称错误成本最低的阶段：一旦事件进入你的 drain，错误的键就已经被索引、被查询，并且已经出现在某人的仪表板中。

## 内部字段

有些字段由 evlog 自行设置。无论你的类型如何，通过 `InternalFields`，`status` 和 `service` 始终会被接受：

```typescript [services/checkout.ts]
log.set({ status: 200 })    // OK - internal field
log.set({ service: 'api' }) // OK - internal field
```

你不需要在接口中包含 `status` 或 `service`。

## 非类型化用法

不使用泛型时，`useLogger` 仍然接受任意字段：

<framework-tabs>

```typescript [Nuxt]server/api/example.ts
import { useLogger } from 'evlog'

const log = useLogger(event)
log.set({ anything: true, nested: { deep: 'value' } }) // OK
```

```typescript [Next.js]app/api/example/route.ts
import { useLogger } from '@/lib/evlog'

const log = useLogger()

log.set({ anything: true, nested: { deep: 'value' } }) // OK
```

```typescript [Hono]src/index.ts
const log = c.get('log')

log.set({ anything: true, nested: { deep: 'value' } }) // OK
```

```typescript [Express]src/index.ts
const log = req.log

log.set({ anything: true, nested: { deep: 'value' } }) // OK
```

</framework-tabs>

类型化字段是完全可选的。

## Nuxt 自动导入

<callout color="warning" icon="i-lucide-triangle-alert">

使用 `useLogger<T>` 的类型化字段需要显式导入。自动导入无法通过泛型传递多余属性检查，这是 TypeScript 的限制，而不是模块的限制。

</callout>

```typescript [server/api/checkout.post.ts]
// 可行 - 显式导入保留类型检查
import { useLogger } from 'evlog'
const log = useLogger<MyFields>(event)
log.set({ typo: 'oops' }) // TypeScript 错误

// 不可行 - 自动导入丢失多余属性检查
const log = useLogger<MyFields>(event)
log.set({ typo: 'oops' }) // 没有错误（静默接受）
```

不使用类型的用法会保留自动导入。只有在传递泛型时才需要添加显式导入。

## 独立使用

同样的泛型也适用于 `createRequestLogger` 和 `createWorkersLogger`：

<framework-tabs>

```typescript [Standalone]
import { createRequestLogger } from 'evlog'

interface MyFields {
  action: string
  userId: string
}

const log = createRequestLogger<MyFields>({
  method: 'POST',
  path: '/checkout',
})

log.set({ action: 'checkout', userId: '123' }) // 正确
log.set({ unknown: true })                      // TypeScript 错误
```

```typescript [Cloudflare Workers]
import { createWorkersLogger } from 'evlog/workers'

interface MyFields {
  action: string
}

const log = createWorkersLogger<MyFields>(request)
log.set({ action: 'process' }) // 正确
```

</framework-tabs>

## 设计建议

### 每个领域一个接口

按领域而非路由定义字段接口：

```typescript [server/types/log-fields.ts]
export interface AuthFields {
  user: { id: string; email: string; role: string }
  action: string
  mfaUsed: boolean
}

export interface PaymentFields {
  user: { id: string; plan: string }
  order: { id: string; total: number; currency: string }
  payment: { method: string; last4: string }
}
```

```typescript [server/api/auth/login.post.ts]
import { useLogger } from 'evlog'
import type { AuthFields } from '~/server/types/log-fields'

export default defineEventHandler(async (event) => {
  const log = useLogger<AuthFields>(event)
  // ...
})
```

### 保持接口专注

只包含路由实际设置的字段，接口无需完全映射你的数据模型：

```typescript [server/types/evlog.ts]
// 过于宽泛 - 大多数路由不会设置所有字段
interface EverythingFields {
  user: FullUserProfile
  order: CompleteOrder
  payment: PaymentDetails
  shipping: ShippingInfo
}

// 专注 - 仅当前路由设置的字段
interface CheckoutFields {
  user: { id: string; plan: string }
  cart: { items: number; total: number }
}
```

## 后续步骤

- [宽事件](https://evlog.zhcndoc.com/learn/wide-events): 使用上下文分层设计有效的宽事件
- [最佳实践](https://evlog.zhcndoc.com/reference/best-practices): 防止敏感数据泄露的安全指南
- [配置](https://evlog.zhcndoc.com/reference/configuration): 所有 `initLogger` 和中间件选项。

---

- [宽事件](https://evlog.zhcndoc.com/learn/wide-events)
- [最佳实践](https://evlog.zhcndoc.com/reference/best-practices)


## Sitemap

See the full [sitemap](https://evlog.zhcndoc.com/sitemap.md) for all pages.
