Skip to content

Commit 619401f

Browse files
authored
Merge pull request #1503 from wvq/numeric-enum
feat: add `t.NumericEnum()` for numeric enum query handling
2 parents 8c66cf0 + de9f4bf commit 619401f

File tree

4 files changed

+65
-2
lines changed

4 files changed

+65
-2
lines changed

src/type-system/index.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import type {
2020
JavaScriptTypeBuilder,
2121
StringOptions,
2222
TUnsafe,
23-
Uint8ArrayOptions
23+
Uint8ArrayOptions,
24+
TEnum
2425
} from '@sinclair/typebox'
2526

2627
import {
@@ -40,7 +41,8 @@ import {
4041
TForm,
4142
TUnionEnum,
4243
ElysiaTransformDecodeBuilder,
43-
TArrayBuffer
44+
TArrayBuffer,
45+
AssertNumericEnum
4446
} from './types'
4547

4648
import { ELYSIA_FORM_DATA, form } from '../utils'
@@ -151,6 +153,23 @@ export const ElysiaType = {
151153
.Encode((value) => value) as any as TNumber
152154
},
153155

156+
NumericEnum<T extends AssertNumericEnum<T>>(item: T, property?: SchemaOptions) {
157+
const schema = Type.Enum(item, property)
158+
const compiler = compile(schema)
159+
160+
return t
161+
.Transform(
162+
t.Union([t.String({ format: 'numeric' }), t.Number()], property)
163+
)
164+
.Decode((value) => {
165+
const number = +value
166+
if (isNaN(number)) throw compiler.Error(number)
167+
if (!compiler.Check(number)) throw compiler.Error(number)
168+
return number
169+
})
170+
.Encode((value) => value) as any as TEnum<T>
171+
},
172+
154173
Integer: (property?: IntegerOptions): TInteger => {
155174
const schema = Type.Integer(property)
156175
const compiler = compile(schema)
@@ -607,6 +626,7 @@ t.ObjectString = ElysiaType.ObjectString
607626
t.ArrayString = ElysiaType.ArrayString
608627
t.ArrayQuery = ElysiaType.ArrayQuery
609628
t.Numeric = ElysiaType.Numeric
629+
t.NumericEnum = ElysiaType.NumericEnum
610630
t.Integer = ElysiaType.Integer
611631

612632
t.File = (arg) => {

src/type-system/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,13 @@ export interface TTransform<
238238
[TransformKind]: TransformOptions<I, O>
239239
[key: string]: any
240240
}
241+
242+
export type AssertNumericEnum<T extends Record<string, string | number>> = {
243+
[K in keyof T]: K extends number
244+
? string
245+
: K extends `${number}`
246+
? string
247+
: K extends string
248+
? number
249+
: never
250+
}

test/aot/has-transform.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,15 @@ describe('Has Transform', () => {
9898
expect(hasTransform(schema)).toBe(true)
9999
})
100100

101+
it('Found t.NumericEnum', () => {
102+
const schema = t.Object({
103+
gender: t.NumericEnum({ UNKNOWN: 0, MALE: 1, FEMALE: 2 }),
104+
liyue: t.String()
105+
})
106+
107+
expect(hasTransform(schema)).toBe(true)
108+
})
109+
101110
it('Found t.ObjectString', () => {
102111
const schema = t.Object({
103112
id: t.String(),

test/validator/query.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,30 @@ describe('Query Validator', () => {
142142
expect(res.status).toBe(200)
143143
})
144144

145+
it('parse numeric enum', async () => {
146+
enum Gender {
147+
MALE = 1,
148+
FEMALE = 2,
149+
UNKNOWN = 3
150+
}
151+
152+
const app = new Elysia().get('/', ({ query }) => query, {
153+
query: t.Object({
154+
name: t.String(),
155+
gender: t.NumericEnum(Gender)
156+
})
157+
})
158+
const res = await app.handle(
159+
req(`/?name=sucrose&gender=${Gender.MALE}`)
160+
)
161+
162+
expect(await res.json()).toEqual({
163+
name: 'sucrose',
164+
gender: Gender.MALE
165+
})
166+
expect(res.status).toBe(200)
167+
})
168+
145169
it('parse single integer', async () => {
146170
const app = new Elysia().get('/', ({ query: { limit } }) => limit, {
147171
query: t.Object({

0 commit comments

Comments
 (0)