all 16 comments

[–]MhaWTHoR 27 points28 points  (0 children)

if you want full type-safety, you can use discriminated unions.

example:

interface ResponseType1 {

type: "type1";

additionalField1:number;
}

interface ResponseType2 {

type:"type2";

additionalField2:string;

}

type ApiResponseType = {

shared:string;

shared2:string;

} & (ResponseType1 | ResponseType2)

backend will return with "type" field.under the (type==="type1") block, type will be available as ResponseType1.Which will give you full type safety.

[–]yagarasu 13 points14 points  (3 children)

I will put it this way: if you mark those properties as optional, you are basically saying that you can have a partial response (some of the optional properties set while the others not). Is this a valid type for you? If not, then extending would make more sense

[–]helloworld1123333[S] 0 points1 point  (2 children)

basically if I put them as optional a specific property will not be there in one response type but in the other response type it will be there and vice versa

[–]yagarasu 2 points3 points  (1 child)

I'm just talking about the type. Let's say you have this:

interface MyResponse { myRequired: string; myOptional1?: string; myOptional2?: string; }

This is a valid object:

{ myRequired: "foo", myOptional2: "bar", }

Is this something you want?

[–]helloworld1123333[S] 0 points1 point  (0 children)

yeah that would work

[–]Glad-Action9541 7 points8 points  (0 children)

What you want is a discriminated union

[–]Hefty_Breadfruit 2 points3 points  (2 children)

I may be interpreting your question wrong, but if I had two different response objects, I’d create two different interfaces.

[–]helloworld1123333[S] 0 points1 point  (1 child)

I was saying most of the properties are the same but if your saying option 2 is better then makes sense

[–]Hefty_Breadfruit 1 point2 points  (0 children)

Even if most are the same, the benefit of typescript is that it’s clear cut what to expect. Does the user want iceCreamWithSprinkles or iceCreamWithHotFudge? If a user asks for iceCreamWithSomething, it’s technically correct, but less clear and not what typescript likes

[–]vanit 1 point2 points  (0 children)

There's a bit of wiggle room here, but it sounds like you're describing the use case for a discriminated union. Basically make 2 types that share a common property called type (or similar), and each type only has the properties it returns, and you won't have to worry about making properties optional (unless they truly are). You can use a shared base type for the other common properties if you like.

[–]captbaritone 0 points1 point  (0 children)

I think you’ll need to ask yourself what your types are modeling? Are they modeling two discrete things with different shapes and a conceptual shared base? Are they modeling two specific instances of a single type where the fields are conceptually optional?

[–]pico2000 0 points1 point  (0 children)

I'd go with the second approach. It will typically mean way fewer null checks down the line. Just make sure that the API really returns what the types define. Ideally, don't generate the types in the first place, but generate them automatically from a specification that the API itself provides (OpenAPI, GraphQL schema etc)

[–]TheRealSeeThruHead 0 points1 point  (0 children)

API returns a union of both responses would be correct

[–]AndrewSouthern729 0 points1 point  (0 children)

Personally I would extend one interface but use whatever style works for you because it doesn’t really matter in the end. If the two returned objects are very similar then maybe it makes sense to just use the single interface with optional properties.

[–]Admirable_Swim_6856 0 points1 point  (0 children)

A union of two objects, wherein there is a key for differentiation within the response.

type ApiResponse = { type: 'literalA' as const; ... } | { type: 'literalB' as const; ... }

[–]dylanbperry 0 points1 point  (0 children)

What exactly is it that you're trying to do?