DEBUG.WIKI
OnlineSIM:在线接收短信,虚拟号码租用
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →
RackNerd;$21.99/年 · 1GB KVM

Pino 10.4.0 升级后找不到 TimeFn 和 MixinFn 类型

Pino 10.4.0 的显式 namespace 出口改变了内部类型可见性。通过公开 LoggerOptions 取得选项函数契约,保留自定义级别与包装库声明边界。

升级到 Pino 10.4.0 后,日志代码还没运行,TypeScript 就提示 Module '"pino"' has no exported member 'MixinFn',或 Namespace 'pino' has no exported member 'TimeFn'。如果 10.3.1 的同一段配置能通过检查,先看类型出口,不要把它当成日志 transport 没有加载。

10.4.0 在 ambient namespace 中增加了显式出口;原来通过隐式导出可见、但没有写 export 的成员不再对外可见。TimeFn、MixinFn 以及若干占位符辅助类型都属于这类情况。更换 default import 与 named import,无法让一个未公开的类型自动成为公开成员。

从公开选项取得真正需要的函数类型

如果应用只是给 timestamp、mixin 等选项写类型,可以从仍公开的 LoggerOptions 取得对应属性类型,避免依赖 namespace 内部别名:

import pino from 'pino';
import type { LoggerOptions } from 'pino';

type TimestampFn = Exclude<LoggerOptions['timestamp'], boolean | undefined>;
type Mixin = NonNullable<LoggerOptions['mixin']>;

const timestamp: TimestampFn = () => `,"time":${Date.now()}`;
const mixin: Mixin = () => ({ service: 'api' });

const logger = pino({ timestamp, mixin });
logger.info('ready');

timestamp 选项还允许布尔值,所以这里移除了 boolean 和 undefined;mixin 是可选函数,所以用 NonNullable 取出函数部分。不能把它们都换成无参数的 () => object:mixin 实际还接收合并对象、级别和 logger,包装库可能需要这些信息。

上例使用默认级别参数。项目有自定义级别时,应保留原来的 LoggerOptions 泛型及 logger 类型,再取得相应属性,不要为了编译通过丢掉自定义级别的约束。

如果没有独立复用函数类型的需要,也可以直接把完整配置对象标注为公开的 LoggerOptions,让函数在选项位置获得上下文类型。迁移时保留原函数的返回内容,类型改写不会替你校验日志字段是否符合业务要求。

包装 LogFn 的辅助类型不能随意替代

ParseLogFnArgs 等类型涉及格式化占位符及参数推断,不能用 Parameters<某个重载> 或 any[] 自动视作等价替换。包装库确实依赖它们时,应先明确自己的公共 API,保留锁文件和此前可用版本,按依赖政策评估受影响升级是否暂缓。

不要手工修改 node_modules/pino.d.ts,也不要通过模块声明给自己编造一个不存在的出口。后续正式版是否重新公开相关类型,需要核对那个发行包;截至 2026 年 10 月 23 日核对的当前正式版本仍是 10.4.0。

skipLibCheck 不能修复应用源码中导入不存在类型的 TS2305/TS2694。关闭严格检查、删除类型文件,或者把整个日志配置断言为 any,只会失去原来的消费侧检查。

验收应使用项目实际 TypeScript 与模块配置检查消费代码;若本项目也发布声明,还要检查自己的 .d.ts 是否继续引用不可见的 Pino 名称。再按原来的运行入口确认时间字段、mixin 内容与级别规则没有因迁移被改动。