[Types] Consider exposing PropertyNames types
See original GitHub issue🚀 Feature Proposal
Please consider exposing NonFunctionPropertyNames<T>
, FunctionPropertyNames<T>
and ConstructorPropertyNames<T>
Motivation
Sometimes types are not infered as desired. Exposing these types could help developers to provide Typescript hints to infer types in some corner cases
Example
Consider your actual type spyOn
:
function spyOn<T extends {}, M extends NonFunctionPropertyNames<Required<T>>>(
object: T,
method: M,
accessType: 'get'
): SpyInstance<Required<T>[M], []>;
function spyOn<T extends {}, M extends NonFunctionPropertyNames<Required<T>>>(
object: T,
method: M,
accessType: 'set'
): SpyInstance<void, [Required<T>[M]]>;
function spyOn<T extends {}, M extends FunctionPropertyNames<Required<T>>>(
object: T,
method: M
): Required<T>[M] extends (...args: any[]) => any
? SpyInstance<ReturnType<Required<T>[M]>, ArgsType<Required<T>[M]>>
: never;
function spyOn<T extends {}, M extends ConstructorPropertyNames<Required<T>>>(
object: T,
method: M
): Required<T>[M] extends new (...args: any[]) => any
? SpyInstance<InstanceType<Required<T>[M]>, ConstructorArgsType<Required<T>[M]>>
: never;
Consider a mock on global.Date
:
const dateSpy: jest.SpyInstance<Date> = jest.spyOn<
NodeJS.Global,
'Date'
>(global, 'Date');
The actual code throws the following syntax error:
Type 'SpyInstance<string, []>' is not assignable to type 'SpyInstance<Date, any>'.
Types of property 'mock' are incompatible.
Type 'MockContext<string, []>' is not assignable to type 'MockContext<Date, any>'.
Type 'string' is not assignable to type 'Date'.t
Why is this happening?
Well, if we look NodeJS.Global Types:
interface Global {
Array: typeof Array;
ArrayBuffer: typeof ArrayBuffer;
Boolean: typeof Boolean;
Buffer: typeof Buffer;
DataView: typeof DataView;
Date: typeof Date;
...
}
Then, if we look for Date
interface DateConstructor {
new(): Date;
new(value: number | string): Date;
new(year: number, month: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): Date;
(): string;
readonly prototype: Date;
...
}
declare var Date: DateConstructor;
As you can realize, Typescript tries to match spyOn in the order provided in the type file. As long as DateConstructor
is a valid FunctionPropertyNames<T>
, Typescript asumes I’m using the following call:
function spyOn<T extends {}, M extends FunctionPropertyNames<Required<T>>>(
object: T,
method: M
): Required<T>[M] extends (...args: any[]) => any
? SpyInstance<ReturnType<Required<T>[M]>, ArgsType<Required<T>[M]>>
: never;
But as long as Date includes (): string
, Typescript infers its a SpyInstance<string, []>
Solution
Exposing these types (NonFunctionPropertyNames<T>
, FunctionPropertyNames<T>
and ConstructorPropertyNames<T>
) would help us provide hints to Typescript:
const dateSpy: jest.SpyInstance<Date> = jest.spyOn<
NodeJS.Global,
ConstructorPropertyNames<NodeJS.Global>
>(global, 'Date');
Do you think it could be possible? Thank you so much for your time and effort
Issue Analytics
- State:
- Created 3 years ago
- Reactions:6
- Comments:8 (1 by maintainers)
Top GitHub Comments
Interestingly since #12089
jest-mock
is already exportingMethodKeysOf
,PropertyKeysOf
,types which are equivalent toConstructorArgumentsOf
FunctionPropertyNames
,NonFunctionPropertyNames
,(edit: apparentlyConstructorPropertyNames
ConstructorPropertyNames
wasn’t present injest-mock
types, the issue is referencing@types/jest
):https://github.com/facebook/jest/blob/7d4595eb935556b8214e1dda45b0b68dfc67068f/packages/jest-mock/src/index.ts#L36-L41
PR welcome 🙂