diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 45b19e634..a9632f368 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -34,6 +34,13 @@ test('example', { sequential: true }, async () => { /* ... */ }) // [!code --] test('example', { concurrent: false }, async () => { /* ... */ }) // [!code ++] ``` +### Narrower Inference for `test.each` and `describe.each` + +`test.each` and `describe.each` now preserve literal types by default for array cases. +This means callback parameters can be inferred as literal unions (for example `1 | 2`) instead of widened types (for example `number`) without needing `as const`. + +If your callback annotations intentionally reject literal values and relied on wider inference, update those annotations to accept the inferred literal unions. + ### Locators in Commands are Serialized as Objects Locators forwarded to [browser commands](/api/browser/commands) are now serialized as a `SerializedLocator` object instead of a bare selector string. The object exposes two fields: diff --git a/packages/runner/src/types/tasks.ts b/packages/runner/src/types/tasks.ts index 35ee6d841..a62112cdc 100644 --- a/packages/runner/src/types/tasks.ts +++ b/packages/runner/src/types/tasks.ts @@ -397,11 +397,11 @@ interface EachFunctionReturn { } interface TestEachFunction { - (cases: ReadonlyArray): EachFunctionReturn - >(cases: ReadonlyArray): EachFunctionReturn< + (cases: ReadonlyArray): EachFunctionReturn + >(cases: ReadonlyArray): EachFunctionReturn< ExtractEachCallbackArgs > - (cases: ReadonlyArray): EachFunctionReturn + (cases: ReadonlyArray): EachFunctionReturn (...args: [TemplateStringsArray, ...any]): EachFunctionReturn } diff --git a/test/typescript/fixtures/dynamic-title/test/dynamic-title.test-d.ts b/test/typescript/fixtures/dynamic-title/test/dynamic-title.test-d.ts index 19d62fbd2..71095e238 100644 --- a/test/typescript/fixtures/dynamic-title/test/dynamic-title.test-d.ts +++ b/test/typescript/fixtures/dynamic-title/test/dynamic-title.test-d.ts @@ -1,9 +1,19 @@ -import { expectTypeOf, test } from 'vitest' +import { describe, expectTypeOf, test } from 'vitest' test.each(['some-value'])('each: %s', () => { expectTypeOf(1).toEqualTypeOf(2) }) +test.each([1, 2])('each literal number: %s', (num) => { + expectTypeOf(num).toEqualTypeOf<1 | 2>() +}) + +describe.each([1, 2])('describe.each literal number: %s', (num) => { + test('keeps literal union', () => { + expectTypeOf(num).toEqualTypeOf<1 | 2>() + }) +}) + test.for(['some-value'])('for: %s', () => { expectTypeOf(1).toEqualTypeOf(2) }) diff --git a/test/unit/test/each.test.ts b/test/unit/test/each.test.ts index 6592edcd3..675d97140 100644 --- a/test/unit/test/each.test.ts +++ b/test/unit/test/each.test.ts @@ -1,4 +1,4 @@ -import { afterAll, describe, expect, test } from 'vitest' +import { afterAll, describe, expect, expectTypeOf, test } from 'vitest' test.each([ [1, 1, 2], @@ -19,6 +19,10 @@ test.each([ expect(typedB).toBeDefined() }) +test.each([1, 2])('preserves literal unions for test.each', (num) => { + expectTypeOf(num).toEqualTypeOf<1 | 2>() +}) + describe.each([ [1, 1, 2], [1, 2, 3], @@ -37,6 +41,12 @@ describe.each([ }) }) +describe.each([1, 2])('preserves literal unions for describe.each', (num) => { + test('literal union type', () => { + expectTypeOf(num).toEqualTypeOf<1 | 2>() + }) +}) + describe.each([ [1, 'a', '1a'], [1, 'b', '1b'],