Skip to content

Commit

Permalink
feat(helpers)!: stricter checking for function signature passed to `m…
Browse files Browse the repository at this point in the history
…ultiple` (#2563)

Co-authored-by: ST-DDT <ST-DDT@gmx.de>
Co-authored-by: Matt Mayer <152770+matthewmayer@users.noreply.github.com>
  • Loading branch information
3 people committed Mar 3, 2024
1 parent 9348138 commit 2b15f2e
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 14 deletions.
38 changes: 38 additions & 0 deletions docs/guide/upgrading_v9/2563.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
### Stricter checking for function signature passed to `faker.helpers.multiple` method

The `faker.helpers.multiple` method takes a function reference as its first parameter. Previously you may have written code like this to generate multiple values.

```ts
faker.helpers.multiple(faker.date.past, { count: 2 });
```

However this code has a bug - `faker.helpers.multiple` passes the loop index as the second parameter to the method, which in this case would set the `refDate` of the `faker.date.past()` call to 0, making all dates before 1970.

Instead you should generally use a lambda function like

```ts
faker.helpers.multiple(() => faker.date.past(), { count: 2 });
```

to get the desired behavior. In v9.0, we use stricter type-checking in Typescript to detect when a function is called which is not compatible with `(v: unknown, index: number)` which can cause compile-time errors in places where previously there were potential runtime errors.

**Bad**

```ts
faker.helpers.multiple(faker.person.firstName, ...); //
// In Typescript, this is now a compile time error
// Argument of type '(sex?: "female" | "male" | undefined) => string'
// is not assignable to parameter of type '(v: unknown, index: number) => unknown'.
```

**Good**

```ts
faker.helpers.multiple(() => faker.person.firstName(), ...); //
```

The new types also allow for easier use-cases where the index is part of the generated data e.g. as id.

```ts
faker.helpers.multiple((_, index) => ({ id: index, ...}), ...); // [{id: 0, ...}, ...]
```
8 changes: 5 additions & 3 deletions src/modules/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1129,17 +1129,19 @@ export class SimpleHelpersModule extends SimpleModuleBase {
* @template TResult The type of elements.
*
* @param method The method used to generate the values.
* The method will be called with `(_, index)`, to allow using the index in the generated value e.g. as id.
* @param options The optional options object.
* @param options.count The number or range of elements to generate. Defaults to `3`.
*
* @example
* faker.helpers.multiple(faker.person.firstName) // [ 'Aniya', 'Norval', 'Dallin' ]
* faker.helpers.multiple(faker.person.firstName, { count: 3 }) // [ 'Santos', 'Lavinia', 'Lavinia' ]
* faker.helpers.multiple(() => faker.person.firstName()) // [ 'Aniya', 'Norval', 'Dallin' ]
* faker.helpers.multiple(() => faker.person.firstName(), { count: 3 }) // [ 'Santos', 'Lavinia', 'Lavinia' ]
* faker.helpers.multiple((_, i) => `${faker.color.human()}-${i + 1}`) // [ 'orange-1', 'orchid-2', 'sky blue-3' ]
*
* @since 8.0.0
*/
multiple<const TResult>(
method: () => TResult,
method: (v: unknown, index: number) => TResult,
options: {
/**
* The number or range of elements to generate.
Expand Down
24 changes: 24 additions & 0 deletions test/modules/__snapshots__/helpers.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ exports[`helpers > 42 > multiple > with method and count range 1`] = `
]
`;

exports[`helpers > 42 > multiple > with method using index 1`] = `
[
0,
3,
6,
]
`;

exports[`helpers > 42 > multiple > with only method 1`] = `
[
3373557479352566,
Expand Down Expand Up @@ -330,6 +338,14 @@ exports[`helpers > 1211 > multiple > with method and count range 1`] = `
]
`;

exports[`helpers > 1211 > multiple > with method using index 1`] = `
[
0,
3,
6,
]
`;

exports[`helpers > 1211 > multiple > with only method 1`] = `
[
8363366038243348,
Expand Down Expand Up @@ -544,6 +560,14 @@ exports[`helpers > 1337 > multiple > with method and count range 1`] = `
]
`;
exports[`helpers > 1337 > multiple > with method using index 1`] = `
[
0,
3,
6,
]
`;
exports[`helpers > 1337 > multiple > with only method 1`] = `
[
2360108457524098,
Expand Down
41 changes: 30 additions & 11 deletions test/modules/helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,14 @@ describe('helpers', () => {
});

t.describe('multiple', (t) => {
t.it('with only method', faker.number.int)
.it('with method and count', faker.number.int, { count: 5 })
.it('with method and count range', faker.number.int, {
t.it('with only method', () => faker.number.int())
.it('with method and count', () => faker.number.int(), {
count: 5,
})
.it('with method and count range', () => faker.number.int(), {
count: { min: 1, max: 10 },
});
})
.it('with method using index', (_, i) => i * 3);
});
});

Expand Down Expand Up @@ -1144,30 +1147,46 @@ describe('helpers', () => {

describe('multiple()', () => {
it('should generate values from the function with a default length of 3', () => {
const result = faker.helpers.multiple(faker.person.firstName);
const result = faker.helpers.multiple(() => faker.person.firstName());
expect(result).toBeTypeOf('object');
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(3);
});

it('should generate the given amount of values from the function', () => {
const result = faker.helpers.multiple(faker.person.firstName, {
count: 5,
});
const result = faker.helpers.multiple(
() => faker.person.firstName(),
{
count: 5,
}
);
expect(result).toBeTypeOf('object');
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(5);
});

it('should generate a ranged number of values from the function', () => {
const result = faker.helpers.multiple(faker.person.firstName, {
count: { min: 1, max: 10 },
});
const result = faker.helpers.multiple(
() => faker.person.firstName(),
{
count: { min: 1, max: 10 },
}
);
expect(result).toBeTypeOf('object');
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBeGreaterThanOrEqual(1);
expect(result.length).toBeLessThanOrEqual(10);
});

it('should generate values using index of created value', () => {
const result = faker.helpers.multiple((_, i) => i * 2, {
count: 3,
});
expect(result).toBeTypeOf('object');
expect(Array.isArray(result)).toBe(true);
expect(result.length).toBe(3);
expect(result).toStrictEqual([0, 2, 4]);
});
});
}
);
Expand Down

0 comments on commit 2b15f2e

Please sign in to comment.