You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Jest和TypeScript监视手动Mock的ES6类方法并消除TS类型报错?

解决TypeScript下Jest手动Mock类时移除@ts-ignore的问题

方法1:扩展原模块的类型声明

TypeScript支持扩展已有模块的类型,我们可以在测试文件中为./sound-player模块补充mockPlayFile的导出类型,让编译器识别这个导出项:

修改测试文件sound-player-consumer.test.ts:

// 扩展原模块的类型声明
declare module './sound-player' {
  export const mockPlayFile: jest.Mock;
}

// 无需@ts-ignore,正常导入
import SoundPlayer, { mockPlayFile } from './sound-player'
import SoundPlayerConsumer from './sound-player-consumer'

jest.mock('./sound-player')

const MockedSoundPlayer = SoundPlayer as jest.Mock<SoundPlayer>

beforeEach(() => {
  MockedSoundPlayer.mockClear()
  mockPlayFile.mockClear()
})

// 测试用例保持不变
it('We can check if the consumer called the class constructor', () => {
  const soundPlayer = new MockedSoundPlayer()
  const soundPlayerConsumer = new SoundPlayerConsumer(soundPlayer)
  expect(SoundPlayer).toHaveBeenCalledTimes(1)
})

it('We can check if the consumer called a method on the class instance', () => {
  const soundPlayer = new MockedSoundPlayer()
  const soundPlayerConsumer = new SoundPlayerConsumer(soundPlayer)
  soundPlayerConsumer.playSomethingCool()
  expect(mockPlayFile).toHaveBeenCalledWith('something-cool.mp3')
})

这种方式无需修改原文件或Mock文件,仅在测试文件中临时补充类型,适合快速修复。

方法2:直接从Mock目录导入类型

既然jest.mock会将原模块替换为__mocks__下的版本,我们可以直接从Mock目录导入类型和mock方法,再做类型断言:

修改测试文件:

// 直接从Mock目录导入类型与mock方法
import SoundPlayerMock, { mockPlayFile } from './__mocks__/sound-player'
import SoundPlayerConsumer from './sound-player-consumer'
import type SoundPlayer from './sound-player'

jest.mock('./sound-player')

// 断言原模块的默认导出就是Mock类
const MockedSoundPlayer = SoundPlayerMock as jest.Mock<SoundPlayer>

beforeEach(() => {
  MockedSoundPlayer.mockClear()
  mockPlayFile.mockClear()
})

// 测试用例保持不变
it('We can check if the consumer called the class constructor', () => {
  const soundPlayer = new MockedSoundPlayer()
  const soundPlayerConsumer = new SoundPlayerConsumer(soundPlayer)
  expect(SoundPlayerMock).toHaveBeenCalledTimes(1)
})

it('We can check if the consumer called a method on the class instance', () => {
  const soundPlayer = new MockedSoundPlayer()
  const soundPlayerConsumer = new SoundPlayerConsumer(soundPlayer)
  soundPlayerConsumer.playSomethingCool()
  expect(mockPlayFile).toHaveBeenCalledWith('something-cool.mp3')
})

这种方式逻辑更清晰,明确依赖Mock的类型,避免类型不匹配问题。

方法3:重构Mock文件对齐原类类型

调整__mocks__/sound-player.ts,让Mock类完全对齐原类的类型结构,同时导出mock方法:

import type SoundPlayer from '../sound-player';

export const mockPlayFile = jest.fn();

// 创建符合原类类型的Mock类
const SoundPlayerMock: jest.Mock<SoundPlayer> = jest.fn().mockImplementation(() => ({
  foo: 'bar', // 保留原类的属性
  playFile: mockPlayFile,
}));

export default SoundPlayerMock;

然后在测试文件中正常导入即可:

import SoundPlayer, { mockPlayFile } from './sound-player'
import SoundPlayerConsumer from './sound-player-consumer'

jest.mock('./sound-player')

const MockedSoundPlayer = SoundPlayer as jest.Mock<typeof import('./__mocks__/sound-player').default>

beforeEach(() => {
  MockedSoundPlayer.mockClear()
  mockPlayFile.mockClear()
})

// 测试用例保持不变

这种方式让Mock文件本身就与原类类型严格对齐,减少后续类型断言的复杂度,适合大型项目。

总结

三种方案都能解决mockPlayFile未导出的TypeScript编译错误,可根据项目需求选择:

  • 方法1适合快速修复,无需改动其他文件;
  • 方法2逻辑更直观,明确依赖Mock资源;
  • 方法3让Mock类型更严谨,利于长期维护。

内容的提问来源于stack exchange,提问作者kYuZz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 13:03:16