Skip to content

Commit f87e4c3

Browse files
authoredJul 13, 2022
fix(jest-mock): add index signature support for spyOn types (#13013)
1 parent c44de55 commit f87e4c3

File tree

4 files changed

+106
-6
lines changed

4 files changed

+106
-6
lines changed
 

‎CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
- `[jest-changed-files]` Fix a lock-up after repeated invocations ([#12757](https://github.com/facebook/jest/issues/12757))
1010
- `[@jest/expect-utils]` Fix deep equality of ImmutableJS OrderedSets ([#12977](https://github.com/facebook/jest/pull/12977))
11+
- `[jest-mock]` Add index signature support for `spyOn` types ([#13013](https://github.com/facebook/jest/pull/13013))
1112
- `[jest-snapshot]` Fix indentation of awaited inline snapshots ([#12986](https://github.com/facebook/jest/pull/12986))
1213

1314
### Chore & Maintenance

‎packages/jest-mock/__typetests__/mock-functions.test.ts

+55
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,34 @@ const spiedObject = {
279279
},
280280
};
281281

282+
type IndexSpiedObject = {
283+
[key: string]: Record<string, any>;
284+
285+
methodA(): boolean;
286+
methodB(a: string, b: number): void;
287+
methodC: (c: number) => boolean;
288+
methodE: (e: any) => never;
289+
290+
propertyA: {a: string};
291+
};
292+
293+
const indexSpiedObject: IndexSpiedObject = {
294+
methodA() {
295+
return true;
296+
},
297+
methodB(a: string, b: number) {
298+
return;
299+
},
300+
methodC(c: number) {
301+
return true;
302+
},
303+
methodE(e: any) {
304+
throw new Error();
305+
},
306+
307+
propertyA: {a: 'abc'},
308+
};
309+
282310
const spy = spyOn(spiedObject, 'methodA');
283311

284312
expectNotAssignable<Function>(spy); // eslint-disable-line @typescript-eslint/ban-types
@@ -330,3 +358,30 @@ expectType<SpyInstance<(value: string | number | Date) => Date>>(
330358
spyOn(globalThis, 'Date'),
331359
);
332360
expectType<SpyInstance<() => number>>(spyOn(Date, 'now'));
361+
362+
// object with index signatures
363+
364+
expectType<SpyInstance<typeof indexSpiedObject.methodA>>(
365+
spyOn(indexSpiedObject, 'methodA'),
366+
);
367+
expectType<SpyInstance<typeof indexSpiedObject.methodB>>(
368+
spyOn(indexSpiedObject, 'methodB'),
369+
);
370+
expectType<SpyInstance<typeof indexSpiedObject.methodC>>(
371+
spyOn(indexSpiedObject, 'methodC'),
372+
);
373+
expectType<SpyInstance<typeof indexSpiedObject.methodE>>(
374+
spyOn(indexSpiedObject, 'methodE'),
375+
);
376+
377+
expectError(spyOn(indexSpiedObject, 'propertyA'));
378+
379+
expectType<SpyInstance<() => {a: string}>>(
380+
spyOn(indexSpiedObject, 'propertyA', 'get'),
381+
);
382+
expectType<SpyInstance<(value: {a: string}) => void>>(
383+
spyOn(indexSpiedObject, 'propertyA', 'set'),
384+
);
385+
expectError(spyOn(indexSpiedObject, 'propertyA'));
386+
387+
expectError(spyOn(indexSpiedObject, 'notThere'));

‎packages/jest-mock/__typetests__/utility-types.test.ts

+44
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,31 @@ class SomeClass {
3737
}
3838
}
3939

40+
class IndexClass {
41+
[key: string]: Record<string, any>;
42+
43+
propertyB = {b: 123};
44+
private _propertyC = {c: undefined};
45+
#propertyD = 'abc';
46+
47+
constructor(public propertyA: {a: string}) {}
48+
49+
methodA(): void {
50+
return;
51+
}
52+
53+
methodB(b: string): string {
54+
return b;
55+
}
56+
57+
get propertyC() {
58+
return this._propertyC;
59+
}
60+
set propertyC(value) {
61+
this._propertyC = value;
62+
}
63+
}
64+
4065
const someObject = {
4166
SomeClass,
4267

@@ -56,6 +81,17 @@ const someObject = {
5681

5782
type SomeObject = typeof someObject;
5883

84+
type IndexObject = {
85+
[key: string]: Record<string, any>;
86+
87+
methodA(): void;
88+
methodB(b: string): boolean;
89+
methodC: (c: number) => true;
90+
91+
propertyA: {a: 123};
92+
propertyB: {b: 'value'};
93+
};
94+
5995
// ClassLike
6096

6197
expectAssignable<ClassLike>(SomeClass);
@@ -89,15 +125,23 @@ expectType<'SomeClass'>(constructorKeys);
89125
// MethodKeys
90126

91127
declare const classMethods: MethodLikeKeys<SomeClass>;
128+
declare const indexClassMethods: MethodLikeKeys<IndexClass>;
92129
declare const objectMethods: MethodLikeKeys<SomeObject>;
130+
declare const indexObjectMethods: MethodLikeKeys<IndexObject>;
93131

94132
expectType<'methodA' | 'methodB'>(classMethods);
133+
expectType<'methodA' | 'methodB'>(indexClassMethods);
95134
expectType<'methodA' | 'methodB' | 'methodC'>(objectMethods);
135+
expectType<'methodA' | 'methodB' | 'methodC'>(indexObjectMethods);
96136

97137
// PropertyKeys
98138

99139
declare const classProperties: PropertyLikeKeys<SomeClass>;
140+
declare const indexClassProperties: PropertyLikeKeys<IndexClass>;
100141
declare const objectProperties: PropertyLikeKeys<SomeObject>;
142+
declare const indexObjectProperties: PropertyLikeKeys<IndexObject>;
101143

102144
expectType<'propertyA' | 'propertyB' | 'propertyC'>(classProperties);
145+
expectType<string>(indexClassProperties);
103146
expectType<'propertyA' | 'propertyB' | 'someClassInstance'>(objectProperties);
147+
expectType<string>(indexObjectProperties);

‎packages/jest-mock/src/index.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -34,13 +34,13 @@ export type MockFunctionMetadata<
3434
export type ClassLike = {new (...args: any): any};
3535
export type FunctionLike = (...args: any) => any;
3636

37-
export type ConstructorLikeKeys<T> = {
38-
[K in keyof T]: T[K] extends ClassLike ? K : never;
39-
}[keyof T];
37+
export type ConstructorLikeKeys<T> = keyof {
38+
[K in keyof T as T[K] extends ClassLike ? K : never]: T[K];
39+
};
4040

41-
export type MethodLikeKeys<T> = {
42-
[K in keyof T]: T[K] extends FunctionLike ? K : never;
43-
}[keyof T];
41+
export type MethodLikeKeys<T> = keyof {
42+
[K in keyof T as T[K] extends FunctionLike ? K : never]: T[K];
43+
};
4444

4545
export type PropertyLikeKeys<T> = {
4646
[K in keyof T]: T[K] extends FunctionLike

0 commit comments

Comments
 (0)
Please sign in to comment.