From f08bf1665826206adc10bc5c0aa7e8295ac430dd Mon Sep 17 00:00:00 2001 From: pierre Date: Wed, 2 Aug 2023 12:06:21 -1000 Subject: [PATCH 1/2] perf(types): prevent immediate deep and circular resolution for deep mock --- src/Mock.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/Mock.ts b/src/Mock.ts index 71b6e79..3691f6c 100644 --- a/src/Mock.ts +++ b/src/Mock.ts @@ -31,21 +31,24 @@ export interface CalledWithMock extends jest.Mock { } export type MockProxy = { - [K in keyof T]: T[K] extends (...args: infer A) => infer B ? CalledWithMock : T[K]; -} & - T; + [K in keyof T]: T[K] extends (...args: infer A) => infer B + ? T[K] & CalledWithMock + : T[K]; +}; export type DeepMockProxy = { // This supports deep mocks in the else branch - [K in keyof T]: T[K] extends (...args: infer A) => infer B ? CalledWithMock : DeepMockProxy; -} & - T; + [K in keyof T]: T[K] extends (...args: infer A) => infer B + ? T[K] & CalledWithMock + : T[K] & DeepMockProxy; +}; export type DeepMockProxyWithFuncPropSupport = { // This supports deep mocks in the else branch - [K in keyof T]: T[K] extends (...args: infer A) => infer B ? CalledWithMock & DeepMockProxy : DeepMockProxy; -} & - T; + [K in keyof T]: T[K] extends (...args: infer A) => infer B + ? T[K] & CalledWithMock & DeepMockProxy + : T[K] & DeepMockProxy; +} export interface MockOpts { deep?: boolean; From 7cb9815db17435c0d1be319320ff88ea5a8d970f Mon Sep 17 00:00:00 2001 From: pierre Date: Wed, 2 Aug 2023 22:46:30 +0000 Subject: [PATCH 2/2] make it work with private properties --- src/Mock.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/Mock.ts b/src/Mock.ts index 3691f6c..dd8cd03 100644 --- a/src/Mock.ts +++ b/src/Mock.ts @@ -30,25 +30,35 @@ export interface CalledWithMock extends jest.Mock { calledWith: (...args: Y | MatchersOrLiterals) => jest.Mock; } -export type MockProxy = { +export type _MockProxy = { [K in keyof T]: T[K] extends (...args: infer A) => infer B ? T[K] & CalledWithMock : T[K]; }; -export type DeepMockProxy = { +export type MockProxy = _MockProxy & T; + +export type _DeepMockProxy = { // This supports deep mocks in the else branch [K in keyof T]: T[K] extends (...args: infer A) => infer B ? T[K] & CalledWithMock - : T[K] & DeepMockProxy; + : T[K] & _DeepMockProxy; }; -export type DeepMockProxyWithFuncPropSupport = { +// we intersect with T here instead of on the mapped type above to +// prevent immediate type resolution on a recursive type, this will +// help to improve performance for deeply nested recursive mocking +// at the same time, this intersection preserves private properties +export type DeepMockProxy = _DeepMockProxy & T; + +export type _DeepMockProxyWithFuncPropSupport = { // This supports deep mocks in the else branch [K in keyof T]: T[K] extends (...args: infer A) => infer B - ? T[K] & CalledWithMock & DeepMockProxy - : T[K] & DeepMockProxy; -} + ? CalledWithMock & DeepMockProxy + : DeepMockProxy; +}; + +export type DeepMockProxyWithFuncPropSupport = _DeepMockProxyWithFuncPropSupport & T; export interface MockOpts { deep?: boolean;