Open ethndotsh opened 8 months ago
Could've sworn there was a solution here from the author of Typebox. Am I losing it or was it deleted?
@ethndotsh Hi, sorry, I had removed the previous snippet but meant to re-post after I had a deeper look at how Elysia was handling type registration. Here is the snippet again (slightly modified to support deriving the type
keyword)
import { Kind, Static, TSchema, SchemaOptions, TypeRegistry } from '@sinclair/typebox'
// -------------------------------------------------------------------------------------
// TypeRegistry
// -------------------------------------------------------------------------------------
TypeRegistry.Set<TUnionEnum>('UnionEnum', (schema, value) => {
return (typeof value === 'string' || typeof value === 'number') && schema.enum.includes(value as never)
})
// -------------------------------------------------------------------------------------
// TUnionEnum
// -------------------------------------------------------------------------------------
export type TUnionValue = string | number
export interface TUnionEnum<T extends TUnionValue[] = []> extends TSchema {
[Kind]: 'UnionEnum'
static: T[number]
enum: T
}
// -------------------------------------------------------------------------------------
// UnionEnum
// -------------------------------------------------------------------------------------
/** `[Elysia]` Creates a Union type with a enum array schema representation */
export function UnionEnum<T extends TUnionValue[]>(values: [...T], options: SchemaOptions = {}) {
const type = (
values.every(value => typeof value === 'string') ? { type: 'string' } :
values.every(value => typeof value === 'number') ? { type: 'number' } :
{} // mixed string | number
)
return { ...options, [Kind]: 'UnionEnum', ...type, enum: values } as TUnionEnum<T>
}
// -------------------------------------------------------------------------------------
// Usage
// -------------------------------------------------------------------------------------
const T = UnionEnum(['A', 'B', 'C']) // const T = { type: 'string', enum: ['A', 'B', 'C'] }
type T = Static<typeof T> // type T = 'A' | 'B' | 'C'
It should be possible to include the above as a module in your project; with Elysia able to use it so long as the type is registered. I had noted that there is potential here to include the type on Elysia t
for the benefit of documentation (as quite a few OpenAPI / Swagger users ask for this enum
representation specifically for documentation generation)
The above should provide a good reference point :) Hope this helps! S
@ethndotsh Hi, sorry, I had removed the previous snippet but meant to re-post after I had a deeper look at how Elysia was handling type registration. Here is the snippet again (slightly modified to support deriving the
type
keyword)import { Kind, Static, TSchema, SchemaOptions, TypeRegistry } from '@sinclair/typebox' // ------------------------------------------------------------------------------------- // TypeRegistry // ------------------------------------------------------------------------------------- TypeRegistry.Set<TUnionEnum>('UnionEnum', (schema, value) => { return (typeof value === 'string' || typeof value === 'number') && schema.enum.includes(value as never) }) // ------------------------------------------------------------------------------------- // TUnionEnum // ------------------------------------------------------------------------------------- export type TUnionValue = string | number export interface TUnionEnum<T extends TUnionValue[] = []> extends TSchema { [Kind]: 'UnionEnum' static: T[number] enum: T } // ------------------------------------------------------------------------------------- // UnionEnum // ------------------------------------------------------------------------------------- /** `[Elysia]` Creates a Union type with a enum array schema representation */ export function UnionEnum<T extends TUnionValue[]>(values: [...T], options: SchemaOptions = {}) { const type = ( values.every(value => typeof value === 'string') ? { type: 'string' } : values.every(value => typeof value === 'number') ? { type: 'number' } : {} // mixed string | number ) return { ...options, [Kind]: 'UnionEnum', ...type, enum: values } as TUnionEnum<T> } // ------------------------------------------------------------------------------------- // Usage // ------------------------------------------------------------------------------------- const T = UnionEnum(['A', 'B', 'C']) // const T = { type: 'string', enum: ['A', 'B', 'C'] } type T = Static<typeof T> // type T = 'A' | 'B' | 'C'
It should be possible to include the above as a module in your project; with Elysia able to use it so long as the type is registered. I had noted that there is potential here to include the type on Elysia
t
for the benefit of documentation (as quite a few OpenAPI / Swagger users ask for thisenum
representation specifically for documentation generation)The above should provide a good reference point :) Hope this helps! S
btw
Why you wont do it out of the box?
is it have some restriction?
I add it to Elysia type-system in https://github.com/elysiajs/elysia/pull/808
What is the problem this feature would solve?
Right now if a Typebox enum is passed into the response field on a route it will just be shown as "string" in Swagger UI or Scalar, and in the actual Swagger JSON it is currently represented as:
For it to be properly shown as an enum in Swagger UI and Scalar, this should be changed to generate as:
What is the feature you are proposing to solve the problem?
Adjusting how the values are generated for OpenAPI
What alternatives have you considered?
No response