diff --git a/docs/guide/upgrading_v9/2563.md b/docs/guide/upgrading_v9/2563.md new file mode 100644 index 00000000000..da770de9d8a --- /dev/null +++ b/docs/guide/upgrading_v9/2563.md @@ -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, ...}, ...] +``` diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts index 593d1ede6a8..b21f13e7188 100644 --- a/src/modules/helpers/index.ts +++ b/src/modules/helpers/index.ts @@ -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( - method: () => TResult, + method: (v: unknown, index: number) => TResult, options: { /** * The number or range of elements to generate. diff --git a/test/modules/__snapshots__/helpers.spec.ts.snap b/test/modules/__snapshots__/helpers.spec.ts.snap index af17437ad3b..8ecb47a8a6e 100644 --- a/test/modules/__snapshots__/helpers.spec.ts.snap +++ b/test/modules/__snapshots__/helpers.spec.ts.snap @@ -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, @@ -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, @@ -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, diff --git a/test/modules/helpers.spec.ts b/test/modules/helpers.spec.ts index 1ba43c702dc..5a2294bbc84 100644 --- a/test/modules/helpers.spec.ts +++ b/test/modules/helpers.spec.ts @@ -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); }); }); @@ -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]); + }); }); } );