I played around with routing-controllers
and it's build-in class-transformer ability. I tried to build an interface where I can perform a search query based either on a location id
or location coordinate
. So I intended to use a discriminated union as a body parameter and could not get it to work. (See last console output what I mean by 'not working')
As an example:
interface LocationCoordinates {
type: 'coordinate'
longitude: number
latitude: number
}
interface LocationId {
type: 'id'
id: number
}
class LocationRadius {
data: LocationCoordinates | LocationId
searchRadiusInKm: number
}
// raw input for LocationCoordinates
const rawLocationCoordinates = {
data: {
longitude: 22,
latitude: 33
},
searchRadiusInKm: 30
}
// raw input for LocationId
const rawLocationId = {
data: {
id: 1
},
searchRadiusInKm: 30
}
// transfrom both raw inputs
const realLocationCoordinates = plainToClass(LocationRadius, rawLocationCoordinates);
const realLocationId = plainToClass(LocationRadius, rawLocationId);
console.log({
coordinateType: realLocationCoordinates.data.type, // expect 'coordinate' but got 'undefinded'
idType: realLocationId.data.type // expect 'id' but got 'undefinded'
});
Is there a way to achive this?
You can do this, but you will need some changes:
LocationId
and LocationCoordinates
should be classes@Type
decorator to the input property. This allows class-transformer
to handle the deserialization based on a specific discriminator parameterclass LocationRadius {
@Type(() => Object, {
keepDiscriminatorProperty: true,
discriminator: {
property: "type",
subTypes: [
{ value: LocationCoordinates, name: "coordinate" },
{ value: LocationId, name: "id" }
]
}
})
data: LocationCoordinates | LocationId
searchRadiusInKm: number
}
type
property to your input, in order to allow even TS to discriminate between the union: // raw input for LocationCoordinates
const rawLocationCoordinates = {
data: {
type: "coordinate",
longitude: 22,
latitude: 33
},
searchRadiusInKm: 30
}
You can see the result in this StackBlitz project I set up