UNPKG

bookish-potato-dto

Version:

A TypeScript DTO (Data Transfer Object) parsing and validation library. Define a schema once — get runtime validation and a fully inferred TypeScript type for free.

43 lines (42 loc) 2.24 kB
import { PrimitiveMap } from '../_types'; import { AnyFieldDescriptor, ArrayDtoFieldDescriptor, ArrayFieldDescriptor, BooleanFieldDescriptor, CustomFieldDescriptor, DateFieldDescriptor, DtoFieldDescriptor, EnumFieldDescriptor, IntegerFieldDescriptor, NumberFieldDescriptor, RegexFieldDescriptor, SchemaMap, StringFieldDescriptor } from './field-descriptor.type'; import { DtoDefinition } from './dto-definition.type'; type FieldOutputType<F extends AnyFieldDescriptor> = F extends StringFieldDescriptor ? string : F extends NumberFieldDescriptor ? number : F extends IntegerFieldDescriptor ? number : F extends BooleanFieldDescriptor ? boolean : F extends DateFieldDescriptor ? Date : F extends RegexFieldDescriptor ? string : F extends EnumFieldDescriptor<infer E> ? E : F extends ArrayFieldDescriptor<infer K> ? PrimitiveMap[K][] : F extends DtoFieldDescriptor<infer S> ? InferDto<DtoDefinition<S>> : F extends ArrayDtoFieldDescriptor<infer S> ? Array<InferDto<DtoDefinition<S>>> : F extends CustomFieldDescriptor<infer T> ? T : unknown; type WithNullable<F extends AnyFieldDescriptor, T> = F extends { options: { isNullable: true; }; } ? T | null : T; type InferFieldType<F extends AnyFieldDescriptor> = WithNullable<F, FieldOutputType<F>>; type RequiredKeys<S extends SchemaMap> = { [K in keyof S]: S[K] extends { options: { isOptional: true; }; } ? never : K; }[keyof S]; type OptionalKeys<S extends SchemaMap> = { [K in keyof S]: S[K] extends { options: { isOptional: true; }; } ? K : never; }[keyof S]; /** * Derives the full TypeScript type from a DtoDefinition. * * - Required fields become required properties. * - `isOptional: true` fields become optional (`T | undefined`). * - `isNullable: true` fields become nullable (`T | null`). * * @example * const PersonDto = defineDto({ name: field.string(), age: field.integer() }); * type PersonDto = InferDto<typeof PersonDto>; * // → { name: string; age: number } */ export type InferDto<D extends DtoDefinition> = D extends DtoDefinition<infer S> ? { [K in RequiredKeys<S>]: InferFieldType<S[K]>; } & { [K in OptionalKeys<S>]?: InferFieldType<S[K]>; } : never; export {};