From c45537f6d4f3a28d8be1ebbe03567004c04af145 Mon Sep 17 00:00:00 2001 From: ST-DDT Date: Sun, 25 Feb 2024 22:05:57 +0100 Subject: [PATCH] feat(helpers)!: use const generics where possible (#2685) --- docs/guide/upgrading_v9/2685.md | 16 ++++ package.json | 9 +-- src/modules/helpers/index.ts | 30 ++++---- test/modules/helpers.spec-d.ts | 127 ++++++++++++++++++++++++++++++++ vitest.config.ts | 4 + 5 files changed, 165 insertions(+), 21 deletions(-) create mode 100644 docs/guide/upgrading_v9/2685.md create mode 100644 test/modules/helpers.spec-d.ts diff --git a/docs/guide/upgrading_v9/2685.md b/docs/guide/upgrading_v9/2685.md new file mode 100644 index 00000000000..7e05fac6e53 --- /dev/null +++ b/docs/guide/upgrading_v9/2685.md @@ -0,0 +1,16 @@ +### Usage of TypeScript 5 Features + +_This upgrade is an extension to_ [#1953](./1953.md) + +The helpers module now uses TS5 features, so if you are using Faker with TypeScript, you must use TS5. + +```ts +// v8 +faker.helpers.arrayElement([1, 2, 3]); // number +faker.helpers.arrayElement([1, 2, 3] as const); // 1 | 2 | 3 + +// v9 +faker.helpers.arrayElement([1, 2, 3]); // 1 | 2 | 3 +``` + +If you are unable to upgrade to TS5, you have to keep using Faker v8. diff --git a/package.json b/package.json index 30752acf7df..d38da9422ca 100644 --- a/package.json +++ b/package.json @@ -31,14 +31,7 @@ "type": "module", "main": "dist/index.cjs", "module": "dist/index.js", - "types": "index.d.ts", - "typesVersions": { - ">=4.0": { - "*": [ - "dist/types/*" - ] - } - }, + "types": "dist/types/index.d.ts", "exports": { ".": { "types": "./dist/types/index.d.ts", diff --git a/src/modules/helpers/index.ts b/src/modules/helpers/index.ts index a42da85fe1d..593d1ede6a8 100644 --- a/src/modules/helpers/index.ts +++ b/src/modules/helpers/index.ts @@ -633,7 +633,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 8.0.0 */ - shuffle( + shuffle( list: T[], options: { /** @@ -659,7 +659,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 2.0.1 */ - shuffle( + shuffle( list: ReadonlyArray, options?: { /** @@ -686,7 +686,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 2.0.1 */ - shuffle( + shuffle( list: T[], options?: { /** @@ -697,7 +697,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { inplace?: boolean; } ): T[]; - shuffle(list: T[], options: { inplace?: boolean } = {}): T[] { + shuffle(list: T[], options: { inplace?: boolean } = {}): T[] { const { inplace = false } = options; if (!inplace) { @@ -734,7 +734,10 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 6.0.0 */ - uniqueArray(source: ReadonlyArray | (() => T), length: number): T[] { + uniqueArray( + source: ReadonlyArray | (() => T), + length: number + ): T[] { if (Array.isArray(source)) { const set = new Set(source); const array = [...set]; @@ -813,7 +816,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 6.3.0 */ - maybe( + maybe( callback: () => TResult, options: { /** @@ -845,7 +848,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 6.3.0 */ - objectKey>(object: T): keyof T { + objectKey>(object: T): keyof T { const array: Array = Object.keys(object); return this.arrayElement(array); } @@ -864,7 +867,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 6.3.0 */ - objectValue>(object: T): T[keyof T] { + objectValue>(object: T): T[keyof T] { const key = this.faker.helpers.objectKey(object); return object[key]; } @@ -883,7 +886,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 8.0.0 */ - objectEntry>( + objectEntry>( object: T ): [keyof T, T[keyof T]] { const key = this.faker.helpers.objectKey(object); @@ -904,7 +907,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 6.3.0 */ - arrayElement(array: ReadonlyArray): T { + arrayElement(array: ReadonlyArray): T { // TODO @xDivisionByZerox 2023-04-20: Remove in v9 if (array == null) { throw new FakerError( @@ -941,7 +944,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 8.0.0 */ - weightedArrayElement( + weightedArrayElement( array: ReadonlyArray<{ /** * The weight of the value. @@ -1000,7 +1003,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 6.3.0 */ - arrayElements( + arrayElements( array: ReadonlyArray, count?: | number @@ -1074,6 +1077,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 8.0.0 */ + // This does not use `const T` because enums shouldn't be created on the spot. enumValue>( enumObject: T ): T[keyof T] { @@ -1134,7 +1138,7 @@ export class SimpleHelpersModule extends SimpleModuleBase { * * @since 8.0.0 */ - multiple( + multiple( method: () => TResult, options: { /** diff --git a/test/modules/helpers.spec-d.ts b/test/modules/helpers.spec-d.ts new file mode 100644 index 00000000000..11ce8c88df3 --- /dev/null +++ b/test/modules/helpers.spec-d.ts @@ -0,0 +1,127 @@ +import { describe, expectTypeOf, it } from 'vitest'; +import { faker } from '../../src'; + +describe('helpers', () => { + describe('shuffle', () => { + describe('inplace: true', () => { + it('const generic single element', () => { + const actual = faker.helpers.shuffle([1], { inplace: true }); + expectTypeOf(actual).toEqualTypeOf>(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.shuffle([1, 'a', false], { + inplace: true, + }); + expectTypeOf(actual).toEqualTypeOf>(); + }); + }); + + describe('inplace: false', () => { + it('const generic single element', () => { + const actual = faker.helpers.shuffle([1], { inplace: false }); + expectTypeOf(actual).toEqualTypeOf>(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.shuffle([1, 'a', false], { + inplace: false, + }); + expectTypeOf(actual).toEqualTypeOf>(); + }); + }); + }); + + describe('uniqueArray', () => { + it('const generic single element', () => { + const actual = faker.helpers.uniqueArray([1], 1); + expectTypeOf(actual).toEqualTypeOf>(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.uniqueArray([1, 'a', false], 3); + expectTypeOf(actual).toEqualTypeOf>(); + }); + }); + + describe('maybe', () => { + it('const generic single element', () => { + // TODO @ST-DDT 2024-02-25: Check why this is detected as `number` instead of `1` + const actual = faker.helpers.maybe(() => 1); + expectTypeOf(actual).toEqualTypeOf(); + }); + }); + + describe('objectKey', () => { + it('const generic single element', () => { + const actual = faker.helpers.objectKey({ a: 1 }); + expectTypeOf(actual).toEqualTypeOf<'a'>(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.objectKey({ a: 1, b: 'a', c: false }); + expectTypeOf(actual).toEqualTypeOf<'a' | 'b' | 'c'>(); + }); + }); + + describe('objectValue', () => { + it('const generic single element', () => { + const actual = faker.helpers.objectValue({ a: 1 }); + expectTypeOf(actual).toEqualTypeOf<1>(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.objectValue({ a: 1, b: 'a', c: false }); + expectTypeOf(actual).toEqualTypeOf<1 | 'a' | false>(); + }); + }); + + describe('objectEntry', () => { + it('const generic single element', () => { + const actual = faker.helpers.objectEntry({ a: 1 }); + expectTypeOf(actual).toEqualTypeOf<['a', 1]>(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.objectEntry({ a: 1, b: 'a', c: false }); + // TODO @ST-DDT 2024-02-25: Check whether we can infer the return type any better + expectTypeOf(actual).toEqualTypeOf<['a' | 'b' | 'c', false | 1 | 'a']>(); + }); + }); + + describe('arrayElement', () => { + it('const generic single element', () => { + const actual = faker.helpers.arrayElement([1]); + expectTypeOf(actual).toEqualTypeOf<1>(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.arrayElement([1, 'a', false]); + expectTypeOf(actual).toEqualTypeOf<1 | 'a' | false>(); + }); + }); + + describe('arrayElements', () => { + it('const generic single element', () => { + const actual = faker.helpers.arrayElements([1], 1); + expectTypeOf(actual).toEqualTypeOf>(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.arrayElements([1, 'a', false], 3); + expectTypeOf(actual).toEqualTypeOf>(); + }); + }); + + describe('multiple', () => { + it('const generic single element', () => { + const actual = faker.helpers.multiple(() => 1); + expectTypeOf(actual).toEqualTypeOf(); + }); + + it('const generic multiple elements', () => { + const actual = faker.helpers.multiple(() => 1, { count: 3 }); + expectTypeOf(actual).toEqualTypeOf(); + }); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index e903e970670..2bac6031fc0 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -19,6 +19,10 @@ export default defineConfig({ seed: VITEST_SEQUENCE_SEED, shuffle: true, }, + typecheck: { + enabled: true, + include: ['test/**/*.spec-d.ts'], + }, onConsoleLog(log, type) { if ( type === 'stderr' &&