import type { Dayjs } from 'dayjs'; import dayjs from 'dayjs'; type DateRange = [Dayjs, Dayjs]; type OptionalDateRangeField< TFormValues extends Record, TRangeField extends keyof TFormValues, > = Record extends Pick ? [Exclude] extends [never] ? never : Exclude extends DateRange ? TRangeField : never : never; type DateRangeSubmitValues< TFormValues extends Record, TRangeField extends keyof TFormValues, TStartField extends string, TEndField extends string, > = Omit & Record; interface DateRangeCodecOptions< TFormValues extends Record, TRangeField extends keyof TFormValues & string, TStartField extends string, TEndField extends string, > { endField: TEndField; rangeField: OptionalDateRangeField & TRangeField; startField: TStartField; } export function createDateRangeCodec< TFormValues extends Record, >() { return function createCodec< TRangeField extends keyof TFormValues & string, TStartField extends string, TEndField extends string, >( options: DateRangeCodecOptions< TFormValues, TRangeField, TStartField, TEndField >, ) { const { endField, startField } = options; const rangeField = options.rangeField as TRangeField; type SubmitValues = DateRangeSubmitValues< TFormValues, TRangeField, TStartField, TEndField >; return { decode(values: Readonly): TFormValues { const { [endField]: end, [startField]: start, ...formValues } = values; return { ...formValues, ...(start && end ? { [rangeField]: [dayjs(start), dayjs(end)] } : {}), } as TFormValues; }, encode(values: Readonly): SubmitValues { const { [rangeField]: value, ...formValues } = values; const range = value as DateRange | undefined; return { ...formValues, [endField]: range?.[1]?.format('YYYY-MM-DD'), [startField]: range?.[0]?.format('YYYY-MM-DD'), } as SubmitValues; }, }; }; }