jest-community/jest-extended

Type definitions of > v3.2.1 using any type

aukevanleeuwen opened this issue · 0 comments

Bug

  • package version: 3.2.3
  • node version: 16.x
  • npm (or yarn) version: yarn 1.22.19

What you did:

What happened (please provide anything you think will help):

I want to use the ESLint rule @typescript-eslint/no-unsafe-assignment, even in my test code if at all possible. With v3.2.3 (basically v3.2.2+ I think) this gives lots of errors like this:

       /some/path/to/some/typescript/test/file/booking-entity.it.ts
         209:70  error  Unsafe assignment of an `any` value  @typescript-eslint/no-unsafe-assignment
         265:17  error  Unsafe assignment of an `any` value  @typescript-eslint/no-unsafe-assignment

Relevant code:

    expect(updatedBooking).toEqual({ ...expectedBooking, updatedAt: expect.toBeDateString() });

There are two ways (that I know of) to use a Jest matcher:

  1. expect(something).toBeDateString()
    
  2. expect(something).toEqual({
        // other props
        createdAt: expect.toBeDateString()
    });
    

This second case gives the linting warnings obviously. I guess the typing is quite difficult to get right, because it seems the return type depends on the context of how it's used. I doubt we can type that properly. However, it seems to be that the first case (1) is pretty much always void. If would do const foo = expect(something).toBeDateString();, foo would be undefined. In the other case: I don't know of any usage within Jest where you could do: expect.toBeDateString().andThenSomeChainedMethodOrProperty(). Is there?

If not: wouldn't the pragmatic way of typing this matchers not be (in this case): string?

I can imagine that, since this is technically not correct, people would object to this, but I don't like any either. Maybe we can settle for unknown instead? I think that would at least make my linter happy.


N.B. more verbose stuff below.

The reason why string would be nice is that if you have the following:

interface Foo {
  // other properties
  createdAt: string;
}

// with following test (structure) - doing this by heart
const actual = sut.somethingThatReturnsAFoo();

//              ↓↓↓
const expected: Foo = {
  // other properties
  createdAt: expect.toBeDateString(), // ← TypeScript will complain here
}

expect(actual).toEqual(expected);

Typescript will complain because expect.toBeDateString() is not of type string, but it is quite nice sometimes to have your expected typed, so that you don't make any typos in other properties for example. I understand that I should do

const expected: Foo = {
  // other properties
  createdAt: expect.toBeDateString() as string,
}

in that case, because in fact it is some kind of (asymmetric?) matcher or something special that Jest knows what to do with. Just saying that if somebody would come up with that matcher type it would probably be even worse, because the as string will then need another cast since that matcher type and string will not have any overlap. So then it would be as unknown as string 😬 . Again, I would mostly favor the pragmatic approach of saying: from a usage perspective this is essentially a string (for this specific matcher, obviously it differs per extended matcher).