UNPKG

nestjs-typebox

Version:

This library provides helper utilities for writing and validating NestJS APIs using [TypeBox](https://github.com/sinclairzx81/typebox) as an alternative to class-validator/class-transformer. Can be configured to patch @nestjs/swagger allowing OpenAPI gene

174 lines (150 loc) 6.14 kB
import { SchemaOptions, Static, StringOptions, TLiteral, TObject, TPropertyKey, TSchema, TUnion, Type } from '@sinclair/typebox/type'; import { AllKeys, Obj, TPartialSome } from './types.js'; export const capitalize = <S extends string>(str: S): Capitalize<S> => { return (str.charAt(0).toUpperCase() + str.slice(1)) as Capitalize<S>; }; export const isObj = (obj: unknown): obj is Obj => obj !== null && typeof obj === 'object'; export type TUnionOfString<T extends string[], Acc extends TSchema[] = []> = T extends [infer L extends string, ...infer R extends string[]] ? TUnionOfString<R, [...Acc, TLiteral<L>]> : Acc; export const LiteralUnion = <const T extends string[]>(values: [...T], options?: SchemaOptions): TUnion<TUnionOfString<T>> => { return Type.Union( values.map(value => Type.Literal(value)), options ) as never; }; export const PartialSome = <T extends TObject, K extends AllKeys<Static<T>>[]>( schema: T, keys: readonly [...K], options?: SchemaOptions ): TPartialSome<T, K> => { return Type.Composite([Type.Omit(schema, keys), Type.Partial(Type.Pick(schema, keys))], options); }; // NOTE: Latest version of typebox makes Omit/Pick distributive by default, but loses strongly typed keys export const DistOmit = <T extends TSchema, K extends AllKeys<Static<T>>[]>(schema: T, keys: readonly [...K], options?: SchemaOptions) => { return Type.Omit(schema, keys, options); }; export const DistPick = <T extends TSchema, K extends AllKeys<Static<T>>[]>(schema: T, keys: readonly [...K], options?: SchemaOptions) => { return Type.Pick(schema, keys, options); }; export const MaybeArray = <T extends TSchema>(schema: T, options?: SchemaOptions) => Type.Union([schema, Type.Array(schema)], options); export const Nullable = <T extends TSchema>(schema: T, options?: SchemaOptions) => Type.Optional(Type.Union([schema, Type.Null()], options)); /* Current issue with Type.KeyOf() + Generics export const SchemaOverride = <S extends TObject, T extends TObject>(schema: S, overrides: T, options?: ObjectOptions) => { return Type.Composite([Type.Omit(schema, Type.KeyOf(overrides)), overrides], options); }; */ // TODO: figure out a way of building UnionPartialSome without having to explicitly overload // for every tuple length. export function UnionPartialSome<U1 extends TObject, K extends AllKeys<Static<U1>>[]>( union: TUnion<[U1]>, keys: readonly [...K] ): TUnion<[TPartialSome<U1, K>]>; export function UnionPartialSome<U1 extends TObject, U2 extends TObject, K extends AllKeys<Static<U1 | U2>>[]>( union: TUnion<[U1, U2]>, keys: readonly [...K] ): TUnion<[TPartialSome<U1, K>, TPartialSome<U2, K>]>; export function UnionPartialSome<U1 extends TObject, U2 extends TObject, U3 extends TObject, K extends AllKeys<Static<U1 | U2 | U3>>[]>( union: TUnion<[U1, U2, U3]>, keys: readonly [...K] ): TUnion<[TPartialSome<U1, K>, TPartialSome<U2, K>, TPartialSome<U3, K>]>; export function UnionPartialSome< U1 extends TObject, U2 extends TObject, U3 extends TObject, U4 extends TObject, K extends AllKeys<Static<U1 | U2 | U3 | U4>>[], >( union: TUnion<[U1, U2, U3, U4]>, keys: readonly [...K] ): TUnion<[TPartialSome<U1, K>, TPartialSome<U2, K>, TPartialSome<U3, K>, TPartialSome<U4, K>]>; export function UnionPartialSome< U1 extends TObject, U2 extends TObject, U3 extends TObject, U4 extends TObject, K extends AllKeys<Static<U1 | U2 | U3 | U4>>[], >( union: TUnion<[U1, U2, U3, U4]>, keys: readonly [...K] ): TUnion<[TPartialSome<U1, K>, TPartialSome<U2, K>, TPartialSome<U3, K>, TPartialSome<U4, K>]>; export function UnionPartialSome< U1 extends TObject, U2 extends TObject, U3 extends TObject, U4 extends TObject, U5 extends TObject, K extends AllKeys<Static<U1 | U2 | U3 | U4 | U5>>[], >( union: TUnion<[U1, U2, U3, U4, U5]>, keys: readonly [...K] ): TUnion<[TPartialSome<U1, K>, TPartialSome<U2, K>, TPartialSome<U3, K>, TPartialSome<U4, K>, TPartialSome<U5, K>]>; export function UnionPartialSome< U1 extends TObject, U2 extends TObject, U3 extends TObject, U4 extends TObject, U5 extends TObject, U6 extends TObject, K extends AllKeys<Static<U1 | U2 | U3 | U4 | U5 | U6>>[], >( union: TUnion<[U1, U2, U3, U4, U5, U6]>, keys: readonly [...K] ): TUnion<[TPartialSome<U1, K>, TPartialSome<U2, K>, TPartialSome<U3, K>, TPartialSome<U4, K>, TPartialSome<U5, K>, TPartialSome<U6, K>]>; export function UnionPartialSome< U1 extends TObject, U2 extends TObject, U3 extends TObject, U4 extends TObject, U5 extends TObject, U6 extends TObject, U7 extends TObject, K extends AllKeys<Static<U1 | U2 | U3 | U4 | U5 | U6 | U7>>[], >( union: TUnion<[U1, U2, U3, U4, U5, U6, U7]>, keys: readonly [...K] ): TUnion< [ TPartialSome<U1, K>, TPartialSome<U2, K>, TPartialSome<U3, K>, TPartialSome<U4, K>, TPartialSome<U5, K>, TPartialSome<U6, K>, TPartialSome<U7, K>, ] >; export function UnionPartialSome< U1 extends TObject, U2 extends TObject, U3 extends TObject, U4 extends TObject, U5 extends TObject, U6 extends TObject, U7 extends TObject, U8 extends TObject, K extends AllKeys<Static<U1 | U2 | U3 | U4 | U5 | U6 | U7 | U8>>[], >( union: TUnion<[U1, U2, U3, U4, U5, U6, U7, U8]>, keys: readonly [...K] ): TUnion< [ TPartialSome<U1, K>, TPartialSome<U2, K>, TPartialSome<U3, K>, TPartialSome<U4, K>, TPartialSome<U5, K>, TPartialSome<U6, K>, TPartialSome<U7, K>, TPartialSome<U8, K>, ] >; export function UnionPartialSome(union: TUnion<TObject[]>, keys: readonly [...TPropertyKey[]]): TUnion { return Type.Union(union.anyOf.map(schema => PartialSome(schema, keys))); } export const IsoDate = (options?: StringOptions) => Type.Transform(Type.String({ format: 'date-time', ...options })) .Decode(value => new Date(value)) .Encode(value => value.toISOString());