Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Types] Consider exposing PropertyNames types #10200

Closed
notaphplover opened this issue Jun 25, 2020 · 8 comments · Fixed by #12442
Closed

[Types] Consider exposing PropertyNames types #10200

notaphplover opened this issue Jun 25, 2020 · 8 comments · Fixed by #12442

Comments

@notaphplover
Copy link
Contributor

notaphplover commented Jun 25, 2020

🚀 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

@SantiEspada
Copy link

any updates on this? It would be really useful!

@vazsonyidl
Copy link

Any updates with this? Would be really useful! 👍🏼

@thedaviddias
Copy link

Any updates on this issue?

@SimenB
Copy link
Member

SimenB commented Feb 10, 2022

PR welcome 🙂

@dandro
Copy link

dandro commented Feb 16, 2022

Is anyone working on this? if not I'd love to give it a go 👍

@SimenB
Copy link
Member

SimenB commented Feb 16, 2022

Go for it!

@mrazauskas
Copy link
Contributor

mrazauskas commented Feb 19, 2022

Interestingly since #12089 jest-mock is already exporting MethodKeysOf, PropertyKeysOf, ConstructorArgumentsOf types which are equivalent to FunctionPropertyNames, NonFunctionPropertyNames, ConstructorPropertyNames (edit: apparently ConstructorPropertyNames wasn’t present in jest-mock types, the issue is referencing @types/jest):

https://github.com/facebook/jest/blob/7d4595eb935556b8214e1dda45b0b68dfc67068f/packages/jest-mock/src/index.ts#L36-L41

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 24, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants