#Can I compare a dynamically created object to a type, to see if it conforms?
34 messages · Page 1 of 1 (latest)
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 } }
}
}
with the builder pattern you could specify what the new type is at each step
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
Yeah, I guess it would kinda have to be runtime, but I would like it to be compile time 😩
This one seems to be the culprit. TS doesn't seem to know that one of those is set, though I'm not sure.
you'll probably need a union there
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?
yeah Required & (Option1 | Option2)
instead of a bunch of optionals that would imply you might have neither
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?
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;
};
};
Can you expand on this one?
here's a really simple example:
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)
...```
if you were mutating it, you could also use asserts
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
// ^?
...```
!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?
use a union or an optional for that extra campaign thing, i guess
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
see this example
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 }
Roger, cheers - I will mull it over a bit, see if I can get it to make sense in my head 😄
Thanks again, will resolve!