aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorST-DDT <[email protected]>2024-02-25 22:05:57 +0100
committerGitHub <[email protected]>2024-02-25 22:05:57 +0100
commitc45537f6d4f3a28d8be1ebbe03567004c04af145 (patch)
treed1f7081187aae812bb355004bbe46439c3094e5f
parent64ff107b8a9cd0965a67f00fd30cded144c02fd6 (diff)
downloadfaker-c45537f6d4f3a28d8be1ebbe03567004c04af145.tar.xz
faker-c45537f6d4f3a28d8be1ebbe03567004c04af145.zip
feat(helpers)!: use const generics where possible (#2685)
-rw-r--r--docs/guide/upgrading_v9/2685.md16
-rw-r--r--package.json9
-rw-r--r--src/modules/helpers/index.ts30
-rw-r--r--test/modules/helpers.spec-d.ts127
-rw-r--r--vitest.config.ts4
5 files changed, 165 insertions, 21 deletions
diff --git a/docs/guide/upgrading_v9/2685.md b/docs/guide/upgrading_v9/2685.md
new file mode 100644
index 00000000..7e05fac6
--- /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 30752acf..d38da942 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 a42da85f..593d1ede 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<T>(
+ shuffle<const T>(
list: T[],
options: {
/**
@@ -659,7 +659,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 2.0.1
*/
- shuffle<T>(
+ shuffle<const T>(
list: ReadonlyArray<T>,
options?: {
/**
@@ -686,7 +686,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 2.0.1
*/
- shuffle<T>(
+ shuffle<const T>(
list: T[],
options?: {
/**
@@ -697,7 +697,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
inplace?: boolean;
}
): T[];
- shuffle<T>(list: T[], options: { inplace?: boolean } = {}): T[] {
+ shuffle<const T>(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<T>(source: ReadonlyArray<T> | (() => T), length: number): T[] {
+ uniqueArray<const T>(
+ source: ReadonlyArray<T> | (() => T),
+ length: number
+ ): T[] {
if (Array.isArray(source)) {
const set = new Set<T>(source);
const array = [...set];
@@ -813,7 +816,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 6.3.0
*/
- maybe<TResult>(
+ maybe<const TResult>(
callback: () => TResult,
options: {
/**
@@ -845,7 +848,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 6.3.0
*/
- objectKey<T extends Record<string, unknown>>(object: T): keyof T {
+ objectKey<const T extends Record<string, unknown>>(object: T): keyof T {
const array: Array<keyof T> = Object.keys(object);
return this.arrayElement(array);
}
@@ -864,7 +867,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 6.3.0
*/
- objectValue<T extends Record<string, unknown>>(object: T): T[keyof T] {
+ objectValue<const T extends Record<string, unknown>>(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<T extends Record<string, unknown>>(
+ objectEntry<const T extends Record<string, unknown>>(
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<T>(array: ReadonlyArray<T>): T {
+ arrayElement<const T>(array: ReadonlyArray<T>): 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<T>(
+ weightedArrayElement<const T>(
array: ReadonlyArray<{
/**
* The weight of the value.
@@ -1000,7 +1003,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 6.3.0
*/
- arrayElements<T>(
+ arrayElements<const T>(
array: ReadonlyArray<T>,
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<T extends Record<string | number, string | number>>(
enumObject: T
): T[keyof T] {
@@ -1134,7 +1138,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 8.0.0
*/
- multiple<TResult>(
+ multiple<const TResult>(
method: () => TResult,
options: {
/**
diff --git a/test/modules/helpers.spec-d.ts b/test/modules/helpers.spec-d.ts
new file mode 100644
index 00000000..11ce8c88
--- /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<Array<1>>();
+ });
+
+ it('const generic multiple elements', () => {
+ const actual = faker.helpers.shuffle([1, 'a', false], {
+ inplace: true,
+ });
+ expectTypeOf(actual).toEqualTypeOf<Array<1 | 'a' | false>>();
+ });
+ });
+
+ describe('inplace: false', () => {
+ it('const generic single element', () => {
+ const actual = faker.helpers.shuffle([1], { inplace: false });
+ expectTypeOf(actual).toEqualTypeOf<Array<1>>();
+ });
+
+ it('const generic multiple elements', () => {
+ const actual = faker.helpers.shuffle([1, 'a', false], {
+ inplace: false,
+ });
+ expectTypeOf(actual).toEqualTypeOf<Array<1 | 'a' | false>>();
+ });
+ });
+ });
+
+ describe('uniqueArray', () => {
+ it('const generic single element', () => {
+ const actual = faker.helpers.uniqueArray([1], 1);
+ expectTypeOf(actual).toEqualTypeOf<Array<1>>();
+ });
+
+ it('const generic multiple elements', () => {
+ const actual = faker.helpers.uniqueArray([1, 'a', false], 3);
+ expectTypeOf(actual).toEqualTypeOf<Array<1 | 'a' | false>>();
+ });
+ });
+
+ 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<number | undefined>();
+ });
+ });
+
+ 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<Array<1>>();
+ });
+
+ it('const generic multiple elements', () => {
+ const actual = faker.helpers.arrayElements([1, 'a', false], 3);
+ expectTypeOf(actual).toEqualTypeOf<Array<1 | 'a' | false>>();
+ });
+ });
+
+ describe('multiple', () => {
+ it('const generic single element', () => {
+ const actual = faker.helpers.multiple(() => 1);
+ expectTypeOf(actual).toEqualTypeOf<number[]>();
+ });
+
+ it('const generic multiple elements', () => {
+ const actual = faker.helpers.multiple(() => 1, { count: 3 });
+ expectTypeOf(actual).toEqualTypeOf<number[]>();
+ });
+ });
+});
diff --git a/vitest.config.ts b/vitest.config.ts
index e903e970..2bac6031 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' &&