#Can I compare a dynamically created object to a type, to see if it conforms?

34 messages · Page 1 of 1 (latest)

willow wagon
#

Stupid newbie question.
But if I have an object that I dynamically build up, with some required fields, and some optional fields, but then at the end I want to assert of it conforms to a more strict type. Is that possible?

willow wagon
#

Lets say I have this type, that I build up using a builder pattern, I guess.

interface CDRatorSignupRequestDTO {
    productConfigurationCode: string;
    serviceType: string;
    existingAccountId?: string;
    existingBillingGroupId?: string;
    subscriptionOwnerIndex: number;
    billingGroupOwnerIndex: number;
    accountOwnerIndex: number;
    billingAddressIndex: number;
    accountInformation?: {
        accountTypeId: string;
    }
    billingGroupInformation?: {
        invoiceTypeId: string;
        customAttributes?: {
            ADYEN_SHOPPER_REFERENCE: {
                type: string;
                value: string;
            }
        }
    }
    users: SignupUser[];
    rechargeTicket?: {
        expMonth?: string;
        expYear?: string;
        lastDigits?: string;
        creditCardType?: string;
        rechargeTicketTypeKey: "BALANCE_BASED";
        ticketValue: string;
    }
    optionTransformations: {
        optionKey: string;
        type: string;
    }[];
    campaigns?: {
        campaignKey: string;
    }[];
    consentInformation: {
        consents: {
            textId: string;
            selected: boolean;
        }[];
    };
    anumber?: {
        anumber: string;
        reservationToken: string;
    };
    anumberImportRequest?: {
        phoneNumber: string;
    };
}

Can I somehow in the end, assert that it fits the shape of this type:

type ExistingUserSignup
    = RequiredDTOFields
    & Pick<CDRatorSignupRequestDTO, "existingAccountId" | "existingBillingGroupId">
    & CDRatorSignupRequestWithExclusivePhonenumber
    & { users: { id: string }[] };
#

I think my problem might be that this function makes the whole thing fall apart:

const addPhoneNumberToSignupRequest = (request: CDRatorSignupRequestDTO, payload: Payload): CDRatorSignupRequestDTO => {
    if (payload.anumber !== undefined) {
        return { ...request, anumber: { anumber: payload.anumber.anumber, reservationToken: payload.anumber.reservationToken } }
    } else {
        return { ...request, anumberImportRequest: { phoneNumber: payload.anumberImportRequest!.phoneNumber } }
    }
}
dire atlas
#

but it kinda sounds like you're asking about runtime checks? types don't exist at runtime, so that's not possible unless you're using runtime types like zod or whatever

willow wagon
#

Yeah, I guess it would kinda have to be runtime, but I would like it to be compile time 😩

willow wagon
dire atlas
#

you'll probably need a union there

willow wagon
#

I think I tried that as well. Problem is the request, need either a field named anumber or a field named anumberImportRequest. Im not well versed in TS to solve that. But I guess I could take the main type, and then extend it with one of the two fields, as a union?

dire atlas
#

yeah Required & (Option1 | Option2)

#

instead of a bunch of optionals that would imply you might have neither

willow wagon
#

Going back to the bilder pattern stuff… I think I need to solve it that way, because now I have trouble with another type not being set correctly.
It’s hard to explain for me, but I need to for example also set a user, which also can be two things. But if I fix the return type and the input types of my builder functions I have to call them in an exact order. Is there a way to do it, so I don’t have to do that, wit generics maybe, but still type safe?

dire atlas
#

But if I fix the return type and the input types of my builder functions I have to call them in an exact order.
no, you don't

#

you can just add to the building object

#

that aside; maybe consider using a DU for the "one of" properties?

#

instead of

type NumberData = {
    anumber: {
        anumber: string;
        reservationToken: string;
    };
} | {
    anumberImportRequest?: {
        phoneNumber: string;
    };
};
```use
```ts
type NumberData = {
  anumber: {
    type: "reserved";
    data: string;
    token: string;
  } | {
    type: "import";
    phone: string;
  };
};
willow wagon
dire atlas
#

here's a really simple example:

hollow widgetBOT
#
that_guy977#0

Preview:```ts
const building = {}
// ^?

function defineX<T>(building: T): T & {x: number} {
return {...building, x: 0}
}

function defineY<T>(building: T): T & {y: number} {
return {...building, y: 0}
}

const r1 = defineX(building)
// ^?
const r2 = defineY(r1)
...```

dire atlas
#

if you were mutating it, you could also use asserts

hollow widgetBOT
#
that_guy977#0

Preview:```ts
const building = {}
// ^?

function defineX<T>(building: T): asserts building is T & { x: number } {
building.x = 0;
}

function defineY<T>(building: T): asserts building is T & { y: number } {
building.y = 0;
}

defineX(building)
building
// ^?
...```

willow wagon
#

Ah awesome, thank you very much, I’ll see if I can figure it out!

#

!resolved

willow wagon
#

!unresolve

#

@dire atlas Sorry, I have another question 😄
I have this stupid if clauses, that, as far as I can see, does so that I have to mutate an object, while doing the builder thing, instead of being able to return a new object, with a specifcied type... something like this:

 // initial signup dto
        let dto = createDefaultSignupDTO();
        //
        // productKey
        dto = addOptionTransformationToSignupDTO(dto, payload.productKey, "SELECTION");

        // campaign
        if (payload.subscription.campaign) {
            dto = addCampaignToSignupDTO(dto, payload.subscription.campaign.code);
        }

The stupid campaign if clause does so I can't be sure what type I return. Is there any way out of that?

dire atlas
#

use a union or an optional for that extra campaign thing, i guess

willow wagon
#

Im not sure I follow. I have a few of those optional things, and does that mean I need a union for each one of those? Seems very unwiedly

#

I mostly want to be able to do something like
const withCampaign = addCampaigmToSignupDTO and be able to pass that on to the next builder function. But only if it should be added. But I guess I can't do taht, right? Unless I nest a lot of if elses

#

Aah hold on, maybe I understand

dire atlas
#

if you have a "one of" kind of relation between properties, a union will let you enforce that "one of", and a DU will further let you enforce "exactly one of"

#

but if it's just a singular optional, you could just use an optional for that, a union would be like {} | { maybePresent: string }

willow wagon
#

Roger, cheers - I will mull it over a bit, see if I can get it to make sense in my head 😄

willow wagon
#

Thanks again, will resolve!