diff --git a/src/__tests__/utils.test.ts b/src/__tests__/utils.test.ts new file mode 100644 index 0000000..8a26745 --- /dev/null +++ b/src/__tests__/utils.test.ts @@ -0,0 +1,15 @@ +import {nullishToOptional} from '../utils'; + +describe('nullishToOptional', () => { + it('should return undefined for nullish values', () => { + expect(nullishToOptional(null)).toBeUndefined(); + expect(nullishToOptional(undefined)).toBeUndefined(); + }); + + it('should return the original value for non-nullish values', () => { + expect(nullishToOptional(0)).toBe(0); + expect(nullishToOptional('')).toBe(''); + expect(nullishToOptional(false)).toBe(false); + expect(nullishToOptional('test')).toBe('test'); + }); +}); diff --git a/src/offers/ticket-offer.ts b/src/offers/ticket-offer.ts index a4d84f2..718f566 100644 --- a/src/offers/ticket-offer.ts +++ b/src/offers/ticket-offer.ts @@ -1,11 +1,12 @@ import {z} from 'zod'; +import {nullishToOptional} from '../utils'; /** * FlexDiscountStep in * https://github.com/AtB-AS/sales/blob/main/sales-service/src/flexible.rs */ export const FlexDiscountStep = z.object({ - expires: z.string().nullish(), + expires: z.string().nullish().transform(nullishToOptional), discount: z.number(), }); export type FlexDiscountStep = z.infer; @@ -26,11 +27,11 @@ export type FlexDiscountLadder = z.infer; */ export const SearchOfferPrice = z.object({ originalAmount: z.string(), - originalAmountFloat: z.number().nullish(), + originalAmountFloat: z.number().nullish().transform(nullishToOptional), amount: z.string(), - amountFloat: z.number().nullish(), + amountFloat: z.number().nullish().transform(nullishToOptional), currency: z.string(), - vatGroup: z.string().nullish(), + vatGroup: z.string().nullish().transform(nullishToOptional), }); export type SearchOfferPrice = z.infer; @@ -50,8 +51,8 @@ export enum OfferValidity { */ export const TicketRoute = z.object({ type: z.nativeEnum(OfferValidity), - from: z.string().nullish(), - to: z.string().nullish(), + from: z.string().nullish().transform(nullishToOptional), + to: z.string().nullish().transform(nullishToOptional), }); export type TicketRoute = z.infer; @@ -64,12 +65,12 @@ export const TicketOffer = z.object({ travellerId: z.string(), price: SearchOfferPrice, fareProduct: z.string(), - validFrom: z.string().nullish(), - validTo: z.string().nullish(), - flexDiscountLadder: FlexDiscountLadder.nullish(), + validFrom: z.string().nullish().transform(nullishToOptional), + validTo: z.string().nullish().transform(nullishToOptional), + flexDiscountLadder: FlexDiscountLadder.nullish().transform(nullishToOptional), route: TicketRoute, shouldStartNow: z.boolean(), - available: z.number().nullish(), + available: z.number().nullish().transform(nullishToOptional), }); export type TicketOffer = z.infer; diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..19b419a --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,15 @@ +/** + * For use in cases where you want a value to be "optional" / `T | undefined`, + * but have to accept `null`. + * + * @example + * ```ts + * const Foo = z.string().nullish().transform(nullishToOptional) + * type Foo = z.infer; // `type Foo = string | undefined` + * ``` + */ +export function nullishToOptional( + value: T | null | undefined, +): T | undefined { + return value ?? undefined; +}