I decided not to use union types as you see them usually. Because they aren't well supported in many languages. And I have a better replacement.
One of
This means input will be only one of the specified types. You will ask: "Are union types the same?". Almost, but the implementation is different. Let's look at some examples.
Typescript union types
type Aircraft = {
weight: number,
capitan: string
}
type Car = {
weight: number,
driver: string,
engine: string
}
type Transport = Aircraft | Car
That's union type. To recognize which type of object is, we can check is the engine is not undefined
. But the usual practice is to create a separate field for defining type:
type Aircraft = {
type: 'aircraft',
weight: number,
capitan: string
}
type Car = {
type: 'car'
weight: number,
driver: string,
engine: string
}
type Transport = Aircraft | Car
And check if the type equals 'aircraft' or 'car'
.
What's the problem?
If my lang doesn't support union types I got some limitations. First of all, I should get a dynamic object, then check if it contains a type field, and only then cast them as some type. Not actually cool.
What to do?
We will use OneOf type. This means the object can contain only 1 of the specified types. How to implement:
public class Aircraft
{
public double weight;
public string capitan;
}
public class Car
{
public double weight;
public string driver;
public string engine;
}
public class Transport
{
private Aircraft? aircraft;
private Car? car;
public Aircraft? Aircraft
{
get => aircraft;
set { aircraft = value; car = null; }
}
public Car? Car
{
get => car;
set { car = value; aircraft = null; }
}
public Field(Aircraft aircraft) => this.aircraft = aircraft;
public Field(Car car) => this.car = car;
public static implicit operator Transport(Aircraft aircraft ) => new(aircraft);
public static implicit operator Transport(Car car) => new(car);
}
What did we get? In transport, type can exist only 1 field because on set all other fields will be set null. And we can return Car or Aircraft types because we have implicit casts for them. It's also typesafe because you HAVE TO check if fields are not null. The only bad thing is more code, but Vesna will generate them for you (when will be v1).
It will solve problems of universality. Also checking if is not null
requires fewer resources than comparing strings (like __typename in GraphQl). If you use the same lang on the front and back (maybe tRPC) and will not use something else, then you can use union types. But my opinion is there, I will be glad to discuss more if you want, just write a comment!
Top comments (0)