Skip to content

Commit 628ab32

Browse files
authored
fix(spy): support private method spy types (fix #10172) (#10213)
1 parent b01afd2 commit 628ab32

2 files changed

Lines changed: 44 additions & 4 deletions

File tree

packages/spy/src/index.ts

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,20 @@ export function fn<T extends Procedure | Constructable = Procedure>(
259259
}) as Mock<T>
260260
}
261261

262+
type SpyOnValue<T extends object, K extends keyof any> = K extends keyof Required<T>
263+
? Required<T>[K]
264+
: (T & Record<K, unknown>)[K]
265+
266+
type SpyOnMethod<T extends object, K extends keyof any>
267+
= SpyOnValue<T, K> extends Constructable | Procedure
268+
? SpyOnValue<T, K>
269+
: never
270+
271+
type SpyOnMethodKey<T extends object, K extends keyof any>
272+
= SpyOnValue<T, K> extends Constructable | Procedure
273+
? K
274+
: never
275+
262276
export function spyOn<T extends object, S extends Properties<Required<T>>>(
263277
object: T,
264278
key: S,
@@ -275,7 +289,11 @@ export function spyOn<T extends object, M extends Classes<Required<T>> | Methods
275289
): Required<T>[M] extends Constructable | Procedure
276290
? Mock<Required<T>[M]>
277291
: never
278-
export function spyOn<T extends object, K extends keyof T>(
292+
export function spyOn<T extends object, K extends keyof any>(
293+
object: T,
294+
key: SpyOnMethodKey<T, K>,
295+
): Mock<SpyOnMethod<T, K>>
296+
export function spyOn<T extends object, K extends keyof any>(
279297
object: T,
280298
key: K,
281299
accessor?: 'get' | 'set',
@@ -317,14 +335,14 @@ export function spyOn<T extends object, K extends keyof T>(
317335
// but there's still a value on the object when called
318336
// https://github.com/vitest-dev/vitest/issues/9439
319337
if (original == null && accessType === 'value') {
320-
original = object[key] as unknown as Procedure
338+
original = object[key as unknown as keyof T] as unknown as Procedure
321339
}
322340
}
323341
else if (accessType !== 'value') {
324-
original = () => object[key]
342+
original = () => object[key as unknown as keyof T]
325343
}
326344
else {
327-
original = object[key] as unknown as Procedure
345+
original = object[key as unknown as keyof T] as unknown as Procedure
328346
}
329347

330348
const originalImplementation = ssr && original ? original() : original

test/typescript/test-d/test.test-d.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,28 @@ describe('test', () => {
4444
// @ts-expect-error
4545
vi.spyOn(google, 'sheets').mockReturnValue({ foo: 1234 })
4646
})
47+
48+
test('spyOn private and protected methods compiles', () => {
49+
class TestClass {
50+
private privateMethod(): number {
51+
return 42
52+
}
53+
54+
protected protectedMethod(): string {
55+
return '42'
56+
}
57+
}
58+
59+
const instance = new TestClass()
60+
vi.spyOn(instance, 'privateMethod').mockReturnValue(1)
61+
vi.spyOn(instance, 'protectedMethod').mockReturnValue('1')
62+
// @ts-expect-error private method returns number
63+
vi.spyOn(instance, 'privateMethod').mockReturnValue('1')
64+
// @ts-expect-error protected method returns string
65+
vi.spyOn(instance, 'protectedMethod').mockReturnValue(1)
66+
// @ts-expect-error unknown methods are rejected
67+
vi.spyOn(instance, 'unknownMethod')
68+
})
4769
})
4870

4971
expectTypeOf({ wolk: 'true' }).toHaveProperty('wolk')

0 commit comments

Comments
 (0)