jestjs/jest

[Bug]: `it.each` from `@jest/globals` causes typescript errors with readonly arrays

Samuel-Therrien-Beslogic opened this issue · 2 comments

Version

29.7.0

Steps to reproduce

import { describe, expect, it } from '@jest/globals'

import { generateRandomInteger } from './random.utils'

const flakyTestAttempts = 10_000
const flakyTag = `[flaky - attempts ${flakyTestAttempts}]`

describe('random', () => {
  describe('generateRandomInteger', () => {
    // <snipped more tests>

    /* eslint-disable @typescript-eslint/no-magic-numbers  -- Readonly array */
    const testReverseMinMax = [
      [123_123, 2],
      [123_142_524, -214_124_124],
      [-50, -262],
      [1, 0],
      [1, -1],
    ] as const
    /* eslint-enable @typescript-eslint/no-magic-numbers */

    it.each(testReverseMinMax)(
      `should reverse min and max when min > max ${flakyTag}`,
      (min, max) => {
        expect.hasAssertions()

        for (let index = 0; index < flakyTestAttempts; index += 1) {
          const result = generateRandomInteger(min, max)

          expect(result).toBeGreaterThanOrEqual(max)
          expect(result).toBeLessThan(min)
        }
      },
    )
  })
})

where generateRandomInteger is implemented as:

interface GenerateRandomIntegerOverloads {
  /**
   * Generate a random number between a min (inclusive) and a max (exclusive).
   *
   * Will reverse the min/max when min > max
   *
   * @param min The minimum value (inclusive)
   * @param max The maximum value (exclusive)
   * @returns The random number
   */
  (min: number, max: number): number

  /**
   * Generate a random number between 0 (inclusive) and a max (exclusive).
   *
   * Will reverse the min/max when min > max
   *
   * @param max The maximum value (exclusive)
   * @returns The random number
   */
  (max: number): number
}

export const generateRandomInteger: GenerateRandomIntegerOverloads = (
  min: number,
  max?: number,
) => {
  let finalMin = min
  let finalMax = max

  // We don't have a max. In this case, the min should be the max.
  if (finalMax == null) {
    finalMin = 0
    finalMax = min
  }

  // We reverse the values when min > max
  if (finalMin > finalMax) {
    const temporaryValue = finalMin
    finalMin = finalMax
    finalMax = temporaryValue
  }

  return Math.floor((Math.random() * (finalMax - finalMin)) + finalMin)
}

Expected behavior

Just like using globals from @types/jest@29.5.12 (instead of importing from '@jest/globals', I expect no Typescript error in the above code

Actual behavior

Argument of type '(min: 1 | 123123 | 123142524 | -50, max: 0 | 2 | -214124124 | -262 | -1) => void' is not assignable to parameter of type '(...args: readonly [123123, 2] | readonly [123142524, -214124124] | readonly [-50, -262] | readonly [1, 0] | readonly [1, -1]) => void | TestReturnValuePromise | TestReturnValueGenerator | undefined'.
  Types of parameters 'min' and 'args' are incompatible.
    Type 'readonly [123123, 2] | readonly [123142524, -214124124] | readonly [-50, -262] | readonly [1, 0] | readonly [1, -1]' is not assignable to type '[min: 1 | 123123 | 123142524 | -50, max: 0 | 2 | -214124124 | -262 | -1]'.
      The type 'readonly [123123, 2]' is 'readonly' and cannot be assigned to the mutable type '[min: 1 | 123123 | 123142524 | -50, max: 0 | 2 | -214124124 | -262 | -1]'.ts(2345)

Additional context

There was a very similar issue previously: #12294

Environment

System:
    OS: Windows 11 10.0.22631
    CPU: (16) x64 11th Gen Intel(R) Core(TM) i7-11800H @ 2.30GHz
  Binaries:
    Node: 18.20.2 - C:\Program Files\nodejs\node.EXE
    Yarn: 1.22.19 - ~\AppData\Roaming\npm\yarn.CMD
    npm: 9.4.2 - C:\Program Files\nodejs\npm.CMD

Could you try installing jest@next? Likely this is already fixed in #14565 and is released as latest alpha.

I had to specifically install @jest/globals@next (blame ts-jest for keeping jest@29 dependencies in the dep tree). But yeah, this seems resolved on @jest/globals@30.0.0-alpha.4. Thanks!