Skip to content

Duplicate identifier 'Stub'/'Mocked' in 3.1.0 with skipLibCheck: false #1017

@ghislain-sn

Description

@ghislain-sn

Is there an existing issue for this?

  • I have searched the existing issues

Current behavior

Real issue in my code, explanation, workaround and root cause created with the help of an IA.

Since @suites/unit@3.1.0 and @suites/doubles.jest@3.1.0, projects compiling with skipLibCheck: false fail with TS2300 Duplicate identifier 'Stub' and TS2300 Duplicate identifier 'Mocked'. @suites/unit@3.0.1 works fine.

The cause is that @suites/unit 3.1.0 now exports Stub and Mocked directly from its barrel, while @suites/doubles.jest 3.1.0 still augments the @suites/unit module with the same identifiers via declare module '@suites/unit'. The same names are therefore declared twice in the same module.


Root cause

node_modules/@suites/unit/dist/esm/index.d.ts in 3.1.0:

/// <reference types="@suites/doubles.jest/unit" />
export { TestBed } from './testbed.js';
export type { UnitTestBed, TestBedBuilder, Mocked, SolitaryTestBedBuilder, SociableTestBedBuilder, } from './types.js';
export type { UnitReference, MockOverride } from '@suites/core.unit';
export type { Stub } from '@suites/types.doubles';

node_modules/@suites/doubles.jest/unit.d.ts in 3.1.0 (kept from 3.0.x):

declare module '@suites/unit' {
  export type Stub<TArgs extends any[] = any[]> = JestStub<TArgs>;
  export type Mocked<T> = JestMocked<T>;
  // ...
}

Stub and Mocked are now declared twice in the @suites/unit module — once via direct export, once via module augmentation — which TypeScript reports as a duplicate identifier when lib-check is on.

In 3.0.1, @suites/unit/index.d.ts did not export Stub/Mocked; the only source was the augmentation in @suites/doubles.jest. No conflict.

Suggested fix

Either:

  • (a) drop the direct exports of Stub and Mocked from @suites/unit/index.d.ts (revert to the 3.0.x layout where adapters own these types via augmentation), or
  • (b) drop the declare module '@suites/unit' { export type Stub … } block from each adapter (@suites/doubles.jest, @suites/doubles.sinon, @suites/doubles.vitest, …) and let @suites/unit own them.

Option (b) is probably preferred since Mocked is already exported from @suites/unit/types.js directly.

Workaround

Pin to 3.0.1 with a ~ range in package.json:

"@suites/unit": "~3.0.1",
"@suites/doubles.jest": "~3.0.1",
"@suites/di.nestjs": "~3.0.1"

Environment

  • TypeScript: 5.9.3
  • Node: 22.x
  • OS: Linux
  • tsconfig: skipLibCheck: false, strict: false

Minimum reproduction code

https://github.com/ghislain-sn/suites-issue-types/

Steps to reproduce

mkdir suites-repro && cd suites-repro
npm init -y
npm i -D typescript @suites/unit@3.1.0 @suites/doubles.jest@3.1.0 @suites/di.nestjs@3.1.0 jest @types/jest

tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": false,
    "skipLibCheck": false
  },
  "include": ["src"]
}

src/index.ts:

import { TestBed } from '@suites/unit'
export { TestBed }
$ npx tsc --noEmit
node_modules/@suites/doubles.jest/unit.d.ts:21:15 - error TS2300: Duplicate identifier 'Stub'.

21   export type Stub<TArgs extends any[] = any[]> = JestStub<TArgs>;
                 ~~~~

  node_modules/@suites/unit/dist/esm/index.d.ts:5:15
    5 export type { Stub } from '@suites/types.doubles';
                    ~~~~
    'Stub' was also declared here.

node_modules/@suites/doubles.jest/unit.d.ts:39:15 - error TS2300: Duplicate identifier 'Mocked'.

39   export type Mocked<T> = JestMocked<T>;
                 ~~~~~~

  node_modules/@suites/unit/dist/esm/index.d.ts:3:44
    3 export type { UnitTestBed, TestBedBuilder, Mocked, SolitaryTestBedBuilder, SociableTestBedBuilder, } from './types.js';
                                                 ~~~~~~
    'Mocked' was also declared here.

node_modules/@suites/unit/dist/esm/index.d.ts:3:44 - error TS2300: Duplicate identifier 'Mocked'.

3 export type { UnitTestBed, TestBedBuilder, Mocked, SolitaryTestBedBuilder, SociableTestBedBuilder, } from './types.js';
                                             ~~~~~~

  node_modules/@suites/doubles.jest/unit.d.ts:39:15
    39   export type Mocked<T> = JestMocked<T>;
                     ~~~~~~
    'Mocked' was also declared here.

node_modules/@suites/unit/dist/esm/index.d.ts:5:15 - error TS2300: Duplicate identifier 'Stub'.

5 export type { Stub } from '@suites/types.doubles';
                ~~~~

  node_modules/@suites/doubles.jest/unit.d.ts:21:15
    21   export type Stub<TArgs extends any[] = any[]> = JestStub<TArgs>;
                     ~~~~
    'Stub' was also declared here.


Found 4 errors in 2 files.

Errors  Files
     2  node_modules/@suites/doubles.jest/unit.d.ts:21
     2  node_modules/@suites/unit/dist/esm/index.d.ts:3

Expected behavior

npx tsc --noEmit exits 0 (as it does on 3.0.1).

Suites version

3.1.0

Node.js version

22.22.0

In which operating systems have you tested?

  • macOS
  • Windows
  • Linux

Other

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions