Skip to content

Commit

Permalink
feat: improve type inference for template literals .each syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
MathieuFedrigo committed Feb 26, 2024
1 parent 5daab90 commit 9969aed
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 16 deletions.
25 changes: 20 additions & 5 deletions docs/GlobalAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -1050,7 +1050,7 @@ test.each(table)('table as a variable example', (a, b, expected, extra) => {

#### Template literal

If all values are of the same type, the template literal API will type the arguments correctly:
If all input values are of the same type, the template literal API will type the arguments correctly:

```ts
import {test} from '@jest/globals';
Expand All @@ -1060,12 +1060,27 @@ test.each`
${1} | ${2} | ${3}
${3} | ${4} | ${7}
${5} | ${6} | ${11}
`('template literal example', ({a, b, expected}) => {
// all arguments are of type `number`
`('template literal example same type', ({a, b, expected}) => {
// all arguments are of type `number` because all inputs (a, b, expected) are of type `number`
});
```

Otherwise it will require a generic type argument:
If the inputs have different types, the arguments will be typed as a union of all the input types (ie type of the variables inside the template literal):

```ts
import {test} from '@jest/globals';

test.each`
a | b | expected
${1} | ${2} | ${'three'}
${3} | ${4} | ${'seven'}
${5} | ${6} | ${'eleven'}
`('template literal example different types', ({a, b, expected}) => {
// all arguments are of type `number | string` because some inputs (a, b) are of type `number` and some others (expected) are of type `string`
});
```

Otherwise, if you want each argument to have the right type, you have to explicitly provide the generic type argument:

```ts
import {test} from '@jest/globals';
Expand All @@ -1076,6 +1091,6 @@ test.each<{a: number; b: number; expected: string; extra?: boolean}>`
${3} | ${4} | ${'seven'} | ${false}
${5} | ${6} | ${'eleven'}
`('template literal example', ({a, b, expected, extra}) => {
// without the generic argument in this case types would default to `unknown`
// all arguments are typed as expected, e.g. `a: number`, `expected: string`, `extra: boolean | undefined`
});
```
16 changes: 8 additions & 8 deletions packages/jest-types/__typetests__/each.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ expectType<void>(
${'a'} | ${true}
${'b'} | ${false}
`('some test', ({item, expected}) => {
expectType<unknown>(item);
expectType<unknown>(expected);
expectType<string | boolean>(item);
expectType<string | boolean>(expected);
}),
);
expectType<void>(
Expand Down Expand Up @@ -257,8 +257,8 @@ expectType<void>(
`(
'some test',
({item, expected}) => {
expectType<unknown>(item);
expectType<unknown>(expected);
expectType<string | boolean>(item);
expectType<string | boolean>(expected);
},
1000,
),
Expand Down Expand Up @@ -393,8 +393,8 @@ expectType<void>(
${'a'} | ${true}
${'b'} | ${false}
`('some test', async ({item, expected}) => {
expectType<unknown>(item);
expectType<unknown>(expected);
expectType<string | boolean>(item);
expectType<string | boolean>(expected);
}),
);
expectType<void>(
Expand Down Expand Up @@ -432,8 +432,8 @@ expectType<void>(
`(
'some test',
({item, expected}) => {
expectType<unknown>(item);
expectType<unknown>(expected);
expectType<string | boolean>(item);
expectType<string | boolean>(expected);
},
1000,
),
Expand Down
6 changes: 3 additions & 3 deletions packages/jest-types/src/Global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@ interface Each<EachFn extends TestFn | BlockFn> {
) => void;

// when the table is a template literal
<T = unknown>(
<T extends Array<unknown>>(
strings: TemplateStringsArray,
...expressions: Array<T>
...expressions: T
): (
name: string | NameLike,
fn: (arg: Record<string, T>, done: DoneFn) => ReturnType<EachFn>,
fn: (arg: Record<string, T[number]>, done: DoneFn) => ReturnType<EachFn>,
timeout?: number,
) => void;

Expand Down

0 comments on commit 9969aed

Please sign in to comment.