Skip to content

Commit 1018e71

Browse files
committed
feat: Support constant type
1 parent 2dc5dd0 commit 1018e71

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

docs/api/types.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,38 @@ schema({
8989
});
9090
```
9191

92+
## `constant`
93+
94+
Creates a constant value. Useful for creating discriminated unions with the `oneOf` type.
95+
96+
**Parameters:**
97+
98+
| Name | Type | Attribute |
99+
| ------------------ | ---------------- | --------- |
100+
| `value` | `TValue` | required |
101+
| `options` | `GenericOptions` | optional |
102+
| `options.required` | `boolean` | optional |
103+
104+
**Example:**
105+
106+
```ts
107+
import { schema, types } from 'papr';
108+
109+
schema({
110+
shape: types.oneOf([
111+
types.object({
112+
type: types.constant('circle' as const, { required: true }),
113+
radius: types.number({ required: true }),
114+
}),
115+
types.object({
116+
type: types.constant('rectangle' as const, { required: true }),
117+
width: types.number({ required: true }),
118+
length: types.number({ required: true }),
119+
}),
120+
]),
121+
});
122+
```
123+
92124
## `date`
93125

94126
Creates a date type.

src/__tests__/types.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,29 @@ describe('types', () => {
393393
});
394394
});
395395

396+
describe('constant', () => {
397+
test('default', () => {
398+
const value = types.constant('foo' as const);
399+
400+
expect(value).toEqual({
401+
enum: ['foo'],
402+
});
403+
expectType<'foo' | undefined>(value);
404+
});
405+
406+
test('required', () => {
407+
const value = types.constant('foo' as const, { required: true });
408+
409+
expect(value).toEqual({
410+
enum: ['foo'],
411+
$required: true,
412+
});
413+
expectType<'foo'>(value);
414+
// @ts-expect-error `value` should not be undefined
415+
expectType<typeof value>(undefined);
416+
});
417+
});
418+
396419
describe('object', () => {
397420
test('default', () => {
398421
const value = types.object({

src/types.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,16 @@ function array<Item, Options extends ArrayOptions>(
136136
} as unknown as GetType<NonNullable<Item>[], Options>;
137137
}
138138

139+
function constant<Value, Options extends GenericOptions>(
140+
value: Value,
141+
options?: Options
142+
): GetType<Value, Options> {
143+
return {
144+
...(options?.required ? { $required: true } : {}),
145+
enum: [value],
146+
} as unknown as GetType<Value, Options>;
147+
}
148+
139149
function enumType<Enum, Options extends GenericOptions>(
140150
values: Enum[],
141151
options?: Options
@@ -323,6 +333,32 @@ export default {
323333
*/
324334
boolean: createSimpleType<boolean>('boolean'),
325335

336+
/**
337+
* Creates a constant value. Useful for creating discriminated unions with the `oneOf` type.
338+
*
339+
* @param value {TValue}
340+
* @param [options] {GenericOptions}
341+
* @param [options.required] {boolean}
342+
*
343+
* @example
344+
* import { schema, types } from 'papr';
345+
*
346+
* schema({
347+
* shape: types.oneOf([
348+
* types.object({
349+
* type: types.constant('circle' as const, { required: true }),
350+
* radius: types.number({ required: true }),
351+
* }),
352+
* types.object({
353+
* type: types.constant('rectangle' as const, { required: true }),
354+
* width: types.number({ required: true }),
355+
* length: types.number({ required: true }),
356+
* }),
357+
* ]),
358+
* });
359+
*/
360+
constant,
361+
326362
/**
327363
* Creates a date type.
328364
*

0 commit comments

Comments
 (0)