Skip to content

Commit

Permalink
feat(helpers)!: use const generics where possible (#2685)
Browse files Browse the repository at this point in the history
  • Loading branch information
ST-DDT committed Feb 25, 2024
1 parent 64ff107 commit c45537f
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 21 deletions.
16 changes: 16 additions & 0 deletions docs/guide/upgrading_v9/2685.md
Original file line number Diff line number Diff line change
@@ -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.
9 changes: 1 addition & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
30 changes: 17 additions & 13 deletions src/modules/helpers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -633,7 +633,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 8.0.0
*/
shuffle<T>(
shuffle<const T>(
list: T[],
options: {
/**
Expand All @@ -659,7 +659,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 2.0.1
*/
shuffle<T>(
shuffle<const T>(
list: ReadonlyArray<T>,
options?: {
/**
Expand All @@ -686,7 +686,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 2.0.1
*/
shuffle<T>(
shuffle<const T>(
list: T[],
options?: {
/**
Expand All @@ -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) {
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -813,7 +816,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 6.3.0
*/
maybe<TResult>(
maybe<const TResult>(
callback: () => TResult,
options: {
/**
Expand Down Expand Up @@ -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);
}
Expand All @@ -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];
}
Expand All @@ -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);
Expand All @@ -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(
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1000,7 +1003,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 6.3.0
*/
arrayElements<T>(
arrayElements<const T>(
array: ReadonlyArray<T>,
count?:
| number
Expand Down Expand Up @@ -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] {
Expand Down Expand Up @@ -1134,7 +1138,7 @@ export class SimpleHelpersModule extends SimpleModuleBase {
*
* @since 8.0.0
*/
multiple<TResult>(
multiple<const TResult>(
method: () => TResult,
options: {
/**
Expand Down
127 changes: 127 additions & 0 deletions test/modules/helpers.spec-d.ts
Original file line number Diff line number Diff line change
@@ -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[]>();
});
});
});
4 changes: 4 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' &&
Expand Down

0 comments on commit c45537f

Please sign in to comment.