Skip to content

Commit c45537f

Browse files
authored
feat(helpers)!: use const generics where possible (#2685)
1 parent 64ff107 commit c45537f

5 files changed

Lines changed: 165 additions & 21 deletions

File tree

docs/guide/upgrading_v9/2685.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
### Usage of TypeScript 5 Features
2+
3+
_This upgrade is an extension to_ [#1953](./1953.md)
4+
5+
The helpers module now uses TS5 features, so if you are using Faker with TypeScript, you must use TS5.
6+
7+
```ts
8+
// v8
9+
faker.helpers.arrayElement([1, 2, 3]); // number
10+
faker.helpers.arrayElement([1, 2, 3] as const); // 1 | 2 | 3
11+
12+
// v9
13+
faker.helpers.arrayElement([1, 2, 3]); // 1 | 2 | 3
14+
```
15+
16+
If you are unable to upgrade to TS5, you have to keep using Faker v8.

package.json

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,7 @@
3131
"type": "module",
3232
"main": "dist/index.cjs",
3333
"module": "dist/index.js",
34-
"types": "index.d.ts",
35-
"typesVersions": {
36-
">=4.0": {
37-
"*": [
38-
"dist/types/*"
39-
]
40-
}
41-
},
34+
"types": "dist/types/index.d.ts",
4235
"exports": {
4336
".": {
4437
"types": "./dist/types/index.d.ts",

src/modules/helpers/index.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
633633
*
634634
* @since 8.0.0
635635
*/
636-
shuffle<T>(
636+
shuffle<const T>(
637637
list: T[],
638638
options: {
639639
/**
@@ -659,7 +659,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
659659
*
660660
* @since 2.0.1
661661
*/
662-
shuffle<T>(
662+
shuffle<const T>(
663663
list: ReadonlyArray<T>,
664664
options?: {
665665
/**
@@ -686,7 +686,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
686686
*
687687
* @since 2.0.1
688688
*/
689-
shuffle<T>(
689+
shuffle<const T>(
690690
list: T[],
691691
options?: {
692692
/**
@@ -697,7 +697,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
697697
inplace?: boolean;
698698
}
699699
): T[];
700-
shuffle<T>(list: T[], options: { inplace?: boolean } = {}): T[] {
700+
shuffle<const T>(list: T[], options: { inplace?: boolean } = {}): T[] {
701701
const { inplace = false } = options;
702702

703703
if (!inplace) {
@@ -734,7 +734,10 @@ export class SimpleHelpersModule extends SimpleModuleBase {
734734
*
735735
* @since 6.0.0
736736
*/
737-
uniqueArray<T>(source: ReadonlyArray<T> | (() => T), length: number): T[] {
737+
uniqueArray<const T>(
738+
source: ReadonlyArray<T> | (() => T),
739+
length: number
740+
): T[] {
738741
if (Array.isArray(source)) {
739742
const set = new Set<T>(source);
740743
const array = [...set];
@@ -813,7 +816,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
813816
*
814817
* @since 6.3.0
815818
*/
816-
maybe<TResult>(
819+
maybe<const TResult>(
817820
callback: () => TResult,
818821
options: {
819822
/**
@@ -845,7 +848,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
845848
*
846849
* @since 6.3.0
847850
*/
848-
objectKey<T extends Record<string, unknown>>(object: T): keyof T {
851+
objectKey<const T extends Record<string, unknown>>(object: T): keyof T {
849852
const array: Array<keyof T> = Object.keys(object);
850853
return this.arrayElement(array);
851854
}
@@ -864,7 +867,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
864867
*
865868
* @since 6.3.0
866869
*/
867-
objectValue<T extends Record<string, unknown>>(object: T): T[keyof T] {
870+
objectValue<const T extends Record<string, unknown>>(object: T): T[keyof T] {
868871
const key = this.faker.helpers.objectKey(object);
869872
return object[key];
870873
}
@@ -883,7 +886,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
883886
*
884887
* @since 8.0.0
885888
*/
886-
objectEntry<T extends Record<string, unknown>>(
889+
objectEntry<const T extends Record<string, unknown>>(
887890
object: T
888891
): [keyof T, T[keyof T]] {
889892
const key = this.faker.helpers.objectKey(object);
@@ -904,7 +907,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
904907
*
905908
* @since 6.3.0
906909
*/
907-
arrayElement<T>(array: ReadonlyArray<T>): T {
910+
arrayElement<const T>(array: ReadonlyArray<T>): T {
908911
// TODO @xDivisionByZerox 2023-04-20: Remove in v9
909912
if (array == null) {
910913
throw new FakerError(
@@ -941,7 +944,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
941944
*
942945
* @since 8.0.0
943946
*/
944-
weightedArrayElement<T>(
947+
weightedArrayElement<const T>(
945948
array: ReadonlyArray<{
946949
/**
947950
* The weight of the value.
@@ -1000,7 +1003,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
10001003
*
10011004
* @since 6.3.0
10021005
*/
1003-
arrayElements<T>(
1006+
arrayElements<const T>(
10041007
array: ReadonlyArray<T>,
10051008
count?:
10061009
| number
@@ -1074,6 +1077,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
10741077
*
10751078
* @since 8.0.0
10761079
*/
1080+
// This does not use `const T` because enums shouldn't be created on the spot.
10771081
enumValue<T extends Record<string | number, string | number>>(
10781082
enumObject: T
10791083
): T[keyof T] {
@@ -1134,7 +1138,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
11341138
*
11351139
* @since 8.0.0
11361140
*/
1137-
multiple<TResult>(
1141+
multiple<const TResult>(
11381142
method: () => TResult,
11391143
options: {
11401144
/**

test/modules/helpers.spec-d.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { describe, expectTypeOf, it } from 'vitest';
2+
import { faker } from '../../src';
3+
4+
describe('helpers', () => {
5+
describe('shuffle', () => {
6+
describe('inplace: true', () => {
7+
it('const generic single element', () => {
8+
const actual = faker.helpers.shuffle([1], { inplace: true });
9+
expectTypeOf(actual).toEqualTypeOf<Array<1>>();
10+
});
11+
12+
it('const generic multiple elements', () => {
13+
const actual = faker.helpers.shuffle([1, 'a', false], {
14+
inplace: true,
15+
});
16+
expectTypeOf(actual).toEqualTypeOf<Array<1 | 'a' | false>>();
17+
});
18+
});
19+
20+
describe('inplace: false', () => {
21+
it('const generic single element', () => {
22+
const actual = faker.helpers.shuffle([1], { inplace: false });
23+
expectTypeOf(actual).toEqualTypeOf<Array<1>>();
24+
});
25+
26+
it('const generic multiple elements', () => {
27+
const actual = faker.helpers.shuffle([1, 'a', false], {
28+
inplace: false,
29+
});
30+
expectTypeOf(actual).toEqualTypeOf<Array<1 | 'a' | false>>();
31+
});
32+
});
33+
});
34+
35+
describe('uniqueArray', () => {
36+
it('const generic single element', () => {
37+
const actual = faker.helpers.uniqueArray([1], 1);
38+
expectTypeOf(actual).toEqualTypeOf<Array<1>>();
39+
});
40+
41+
it('const generic multiple elements', () => {
42+
const actual = faker.helpers.uniqueArray([1, 'a', false], 3);
43+
expectTypeOf(actual).toEqualTypeOf<Array<1 | 'a' | false>>();
44+
});
45+
});
46+
47+
describe('maybe', () => {
48+
it('const generic single element', () => {
49+
// TODO @ST-DDT 2024-02-25: Check why this is detected as `number` instead of `1`
50+
const actual = faker.helpers.maybe(() => 1);
51+
expectTypeOf(actual).toEqualTypeOf<number | undefined>();
52+
});
53+
});
54+
55+
describe('objectKey', () => {
56+
it('const generic single element', () => {
57+
const actual = faker.helpers.objectKey({ a: 1 });
58+
expectTypeOf(actual).toEqualTypeOf<'a'>();
59+
});
60+
61+
it('const generic multiple elements', () => {
62+
const actual = faker.helpers.objectKey({ a: 1, b: 'a', c: false });
63+
expectTypeOf(actual).toEqualTypeOf<'a' | 'b' | 'c'>();
64+
});
65+
});
66+
67+
describe('objectValue', () => {
68+
it('const generic single element', () => {
69+
const actual = faker.helpers.objectValue({ a: 1 });
70+
expectTypeOf(actual).toEqualTypeOf<1>();
71+
});
72+
73+
it('const generic multiple elements', () => {
74+
const actual = faker.helpers.objectValue({ a: 1, b: 'a', c: false });
75+
expectTypeOf(actual).toEqualTypeOf<1 | 'a' | false>();
76+
});
77+
});
78+
79+
describe('objectEntry', () => {
80+
it('const generic single element', () => {
81+
const actual = faker.helpers.objectEntry({ a: 1 });
82+
expectTypeOf(actual).toEqualTypeOf<['a', 1]>();
83+
});
84+
85+
it('const generic multiple elements', () => {
86+
const actual = faker.helpers.objectEntry({ a: 1, b: 'a', c: false });
87+
// TODO @ST-DDT 2024-02-25: Check whether we can infer the return type any better
88+
expectTypeOf(actual).toEqualTypeOf<['a' | 'b' | 'c', false | 1 | 'a']>();
89+
});
90+
});
91+
92+
describe('arrayElement', () => {
93+
it('const generic single element', () => {
94+
const actual = faker.helpers.arrayElement([1]);
95+
expectTypeOf(actual).toEqualTypeOf<1>();
96+
});
97+
98+
it('const generic multiple elements', () => {
99+
const actual = faker.helpers.arrayElement([1, 'a', false]);
100+
expectTypeOf(actual).toEqualTypeOf<1 | 'a' | false>();
101+
});
102+
});
103+
104+
describe('arrayElements', () => {
105+
it('const generic single element', () => {
106+
const actual = faker.helpers.arrayElements([1], 1);
107+
expectTypeOf(actual).toEqualTypeOf<Array<1>>();
108+
});
109+
110+
it('const generic multiple elements', () => {
111+
const actual = faker.helpers.arrayElements([1, 'a', false], 3);
112+
expectTypeOf(actual).toEqualTypeOf<Array<1 | 'a' | false>>();
113+
});
114+
});
115+
116+
describe('multiple', () => {
117+
it('const generic single element', () => {
118+
const actual = faker.helpers.multiple(() => 1);
119+
expectTypeOf(actual).toEqualTypeOf<number[]>();
120+
});
121+
122+
it('const generic multiple elements', () => {
123+
const actual = faker.helpers.multiple(() => 1, { count: 3 });
124+
expectTypeOf(actual).toEqualTypeOf<number[]>();
125+
});
126+
});
127+
});

vitest.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export default defineConfig({
1919
seed: VITEST_SEQUENCE_SEED,
2020
shuffle: true,
2121
},
22+
typecheck: {
23+
enabled: true,
24+
include: ['test/**/*.spec-d.ts'],
25+
},
2226
onConsoleLog(log, type) {
2327
if (
2428
type === 'stderr' &&

0 commit comments

Comments
 (0)