Skip to content

Commit 7731a0f

Browse files
Merge pull request #1164 from fabian-hiller/feat-bigint-multiple-of
Add support for bigints to multipleOf validation action
2 parents ca808fa + d326150 commit 7731a0f

11 files changed

Lines changed: 131 additions & 47 deletions

File tree

library/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ All notable changes to the library will be documented in this file.
88
- Add `summarize` method to summarize issues into a pretty-printable multi-line string (pull request #1158)
99
- Add `minEntries` and `maxEntries` validation action to validate number of object entries (pull request #1100)
1010
- Add `entries` and `notEntries` validation action to validate number of object entries (pull request #1156)
11+
- Add support for bigints to `multipleOf` validation action (pull request #1164)
1112
- Change implementation of `variant` and `variantAsync` schema to improve performance by aborting validation of discriminators early (pull request #1110)
1213

1314
## v1.0.0 (March 18, 2025)

library/src/actions/multipleOf/multipleOf.test-d.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,26 @@ describe('multipleOf', () => {
3030
});
3131

3232
describe('should infer correct types', () => {
33-
type Action = MultipleOfAction<number, 10, undefined>;
33+
type Action1 = MultipleOfAction<number, 10, undefined>;
34+
type Action2 = MultipleOfAction<bigint, 10n, undefined>;
3435

3536
test('of input', () => {
36-
expectTypeOf<InferInput<Action>>().toEqualTypeOf<number>();
37+
expectTypeOf<InferInput<Action1>>().toEqualTypeOf<number>();
38+
expectTypeOf<InferInput<Action2>>().toEqualTypeOf<bigint>();
3739
});
3840

3941
test('of output', () => {
40-
expectTypeOf<InferOutput<Action>>().toEqualTypeOf<number>();
42+
expectTypeOf<InferOutput<Action1>>().toEqualTypeOf<number>();
43+
expectTypeOf<InferOutput<Action2>>().toEqualTypeOf<bigint>();
4144
});
4245

4346
test('of issue', () => {
44-
expectTypeOf<InferIssue<Action>>().toEqualTypeOf<
47+
expectTypeOf<InferIssue<Action1>>().toEqualTypeOf<
4548
MultipleOfIssue<number, 10>
4649
>();
50+
expectTypeOf<InferIssue<Action2>>().toEqualTypeOf<
51+
MultipleOfIssue<bigint, 10n>
52+
>();
4753
});
4854
});
4955
});

library/src/actions/multipleOf/multipleOf.test.ts

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import { describe, expect, test } from 'vitest';
22
import type { NumberIssue } from '../../schemas/index.ts';
33
import { expectActionIssue, expectNoActionIssue } from '../../vitest/index.ts';
4-
import {
5-
multipleOf,
6-
type MultipleOfAction,
7-
type MultipleOfIssue,
8-
} from './multipleOf.ts';
4+
import { multipleOf, type MultipleOfAction } from './multipleOf.ts';
95

106
describe('multipleOf', () => {
117
describe('should return action object', () => {
@@ -45,8 +41,6 @@ describe('multipleOf', () => {
4541
});
4642

4743
describe('should return dataset without issues', () => {
48-
const action = multipleOf(5);
49-
5044
test('for untyped inputs', () => {
5145
const issues: [NumberIssue] = [
5246
{
@@ -59,7 +53,7 @@ describe('multipleOf', () => {
5953
},
6054
];
6155
expect(
62-
action['~run']({ typed: false, value: null, issues }, {})
56+
multipleOf(5)['~run']({ typed: false, value: null, issues }, {})
6357
).toStrictEqual({
6458
typed: false,
6559
value: null,
@@ -68,30 +62,52 @@ describe('multipleOf', () => {
6862
});
6963

7064
test('for valid numbers', () => {
71-
expectNoActionIssue(action, [-15, -10, -5, 0, 5, 10, 15]);
65+
expectNoActionIssue(multipleOf(5), [-15, -10, -5, 0, 5, 10, 15]);
66+
});
67+
68+
test('for valid bigints', () => {
69+
expectNoActionIssue(multipleOf(5n), [-15n, -10n, -5n, 0n, 5n, 10n, 15n]);
7270
});
7371
});
7472

7573
describe('should return dataset with issues', () => {
76-
const action = multipleOf(5, 'message');
77-
const baseIssue: Omit<MultipleOfIssue<number, 5>, 'input' | 'received'> = {
74+
const baseIssue = {
7875
kind: 'validation',
7976
type: 'multiple_of',
8077
expected: '%5',
8178
message: 'message',
82-
requirement: 5,
83-
};
79+
} as const;
8480

8581
test('for invalid numbers', () => {
86-
expectActionIssue(action, baseIssue, [-14, -9, -4, 1, 3, 6, 11]);
82+
expectActionIssue(
83+
multipleOf(5, 'message'),
84+
{ ...baseIssue, requirement: 5 },
85+
[-14, -9, -4, 1, 3, 6, 11]
86+
);
8787
});
8888

8989
test('for infinity', () => {
90-
expectActionIssue(action, baseIssue, [-Infinity, Infinity]);
90+
expectActionIssue(
91+
multipleOf(5, 'message'),
92+
{ ...baseIssue, requirement: 5 },
93+
[-Infinity, Infinity]
94+
);
9195
});
9296

9397
test('for NaN', () => {
94-
expectActionIssue(action, baseIssue, [NaN]);
98+
expectActionIssue(
99+
multipleOf(5, 'message'),
100+
{ ...baseIssue, requirement: 5 },
101+
[NaN]
102+
);
103+
});
104+
105+
test('for invalid bigints', () => {
106+
expectActionIssue(
107+
multipleOf(5n, 'message'),
108+
{ ...baseIssue, requirement: 5n },
109+
[-14n, -9n, -4n, 1n, 3n, 6n, 11n]
110+
);
95111
});
96112
});
97113
});

library/src/actions/multipleOf/multipleOf.ts

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ import type {
55
} from '../../types/index.ts';
66
import { _addIssue } from '../../utils/index.ts';
77

8+
/**
9+
* Input type
10+
*/
11+
type Input = number | bigint;
12+
813
/**
914
* Multiple of issue interface.
1015
*/
1116
export interface MultipleOfIssue<
12-
TInput extends number,
13-
TRequirement extends number,
17+
TInput extends Input,
18+
TRequirement extends Input,
1419
> extends BaseIssue<TInput> {
1520
/**
1621
* The issue kind.
@@ -27,7 +32,7 @@ export interface MultipleOfIssue<
2732
/**
2833
* The received property.
2934
*/
30-
readonly received: `${number}`;
35+
readonly received: `${TInput}`;
3136
/**
3237
* The divisor.
3338
*/
@@ -38,8 +43,8 @@ export interface MultipleOfIssue<
3843
* Multiple of action interface.
3944
*/
4045
export interface MultipleOfAction<
41-
TInput extends number,
42-
TRequirement extends number,
46+
TInput extends Input,
47+
TRequirement extends Input,
4348
TMessage extends
4449
| ErrorMessage<MultipleOfIssue<TInput, TRequirement>>
4550
| undefined,
@@ -82,6 +87,18 @@ export function multipleOf<
8287
const TRequirement extends number,
8388
>(requirement: TRequirement): MultipleOfAction<TInput, TRequirement, undefined>;
8489

90+
/**
91+
* Creates a [multiple](https://en.wikipedia.org/wiki/Multiple_(mathematics)) of validation action.
92+
*
93+
* @param requirement The divisor.
94+
*
95+
* @returns A multiple of action.
96+
*/
97+
export function multipleOf<
98+
TInput extends bigint,
99+
const TRequirement extends bigint,
100+
>(requirement: TRequirement): MultipleOfAction<TInput, TRequirement, undefined>;
101+
85102
/**
86103
* Creates a [multiple](https://en.wikipedia.org/wiki/Multiple_(mathematics)) of validation action.
87104
*
@@ -101,14 +118,33 @@ export function multipleOf<
101118
message: TMessage
102119
): MultipleOfAction<TInput, TRequirement, TMessage>;
103120

121+
/**
122+
* Creates a [multiple](https://en.wikipedia.org/wiki/Multiple_(mathematics)) of validation action.
123+
*
124+
* @param requirement The divisor.
125+
* @param message The error message.
126+
*
127+
* @returns A multiple of action.
128+
*/
129+
export function multipleOf<
130+
TInput extends bigint,
131+
const TRequirement extends bigint,
132+
const TMessage extends
133+
| ErrorMessage<MultipleOfIssue<TInput, TRequirement>>
134+
| undefined,
135+
>(
136+
requirement: TRequirement,
137+
message: TMessage
138+
): MultipleOfAction<TInput, TRequirement, TMessage>;
139+
104140
// @__NO_SIDE_EFFECTS__
105141
export function multipleOf(
106-
requirement: number,
107-
message?: ErrorMessage<MultipleOfIssue<number, number>>
142+
requirement: Input,
143+
message?: ErrorMessage<MultipleOfIssue<Input, Input>>
108144
): MultipleOfAction<
109-
number,
110-
number,
111-
ErrorMessage<MultipleOfIssue<number, number>> | undefined
145+
Input,
146+
Input,
147+
ErrorMessage<MultipleOfIssue<Input, Input>> | undefined
112148
> {
113149
return {
114150
kind: 'validation',
@@ -119,7 +155,8 @@ export function multipleOf(
119155
requirement,
120156
message,
121157
'~run'(dataset, config) {
122-
if (dataset.typed && dataset.value % this.requirement !== 0) {
158+
// @ts-expect-error
159+
if (dataset.typed && dataset.value % this.requirement != 0) {
123160
_addIssue(this, 'multiple', dataset, config);
124161
}
125162
return dataset;

library/src/utils/_stringify/_stringify.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export function _stringify(input: unknown): string {
1313
if (type === 'string') {
1414
return `"${input}"`;
1515
}
16+
// TODO: Should we add "n" suffix to bigints?
1617
if (type === 'number' || type === 'bigint' || type === 'boolean') {
1718
return `${input}`;
1819
}

website/src/routes/api/(actions)/multipleOf/index.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ description: Creates a multiple of validation action.
44
source: /actions/multipleOf/multipleOf.ts
55
contributors:
66
- fabian-hiller
7+
- EltonLobo07
78
---
89

910
import { ApiList, Property } from '~/components';
@@ -60,7 +61,7 @@ The following APIs can be combined with `multipleOf`.
6061

6162
### Schemas
6263

63-
<ApiList items={['any', 'custom', 'number', 'unknown']} />
64+
<ApiList items={['any', 'bigint', 'custom', 'number', 'unknown']} />
6465

6566
### Methods
6667

website/src/routes/api/(actions)/multipleOf/properties.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import type { PropertyProps } from '~/components';
33
export const properties: Record<string, PropertyProps> = {
44
TInput: {
55
modifier: 'extends',
6-
type: 'number',
6+
type: {
7+
type: 'union',
8+
options: ['number', 'bigint'],
9+
},
710
},
811
TRequirement: {
912
modifier: 'extends',
10-
type: 'number',
13+
type: {
14+
type: 'union',
15+
options: ['number', 'bigint'],
16+
},
1117
},
1218
TMessage: {
1319
modifier: 'extends',

website/src/routes/api/(schemas)/bigint/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ The following APIs can be combined with `bigint`.
121121
'maxValue',
122122
'metadata',
123123
'minValue',
124+
'multipleOf',
124125
'notValue',
125126
'notValues',
126127
'rawCheck',

website/src/routes/api/(types)/MultipleOfAction/properties.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@ import type { PropertyProps } from '~/components';
33
export const properties: Record<string, PropertyProps> = {
44
TInput: {
55
modifier: 'extends',
6-
type: 'number',
6+
type: {
7+
type: 'union',
8+
options: ['number', 'bigint'],
9+
},
710
},
811
TRequirement: {
912
modifier: 'extends',
10-
type: 'number',
13+
type: {
14+
type: 'union',
15+
options: ['number', 'bigint'],
16+
},
1117
},
1218
TMessage: {
1319
modifier: 'extends',

website/src/routes/api/(types)/MultipleOfIssue/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ title: MultipleOfIssue
33
description: Multiple of issue interface.
44
contributors:
55
- fabian-hiller
6+
- EltonLobo07
67
---
78

89
import { Property } from '~/components';

0 commit comments

Comments
 (0)