We're using Unionize pretty heavily at Unsplash (thank you!). I would like to share some UX feedback relating to how the union types appear when inspected. To do so, I will use an example.
Vanilla tagged union
First let's see how type inspection works for a sans-Unionize union:
type Foo = { foo: number };
type Bar = { bar: number };
type Baz = { baz: number };
type MyNestedUnion =
| {
tag: "NestedFoo";
value: Foo;
}
| {
tag: "NestedBar";
value: Bar;
}
| {
tag: "NestedBaz";
value: Baz;
};
// Hover this
type MyUnion =
| {
tag: "Foo";
value: Foo;
}
| {
tag: "Bar";
value: Bar;
}
| {
tag: "Baz";
value: Baz;
}
| {
tag: "Union";
value: MyNestedUnion;
};
// Hover this
declare const fn: (f: MyUnion) => void;
Hover over type MyUnion and you'll see:
type MyUnion =
| {
tag: "Foo";
value: Foo;
}
| {
tag: "Bar";
value: Bar;
}
| {
tag: "Baz";
value: Baz;
}
| {
tag: "Union";
value: MyNestedUnion;
};
Great! Hover over const fn and you'll see:
const fn: (f: MyUnion) => void;
Great! No problems here.
Unionize union
Now if we define the equivalent tagged union but using Unionize:
import { ofType, unionize, UnionOf } from "unionize";
type Foo = { foo: number };
type Bar = { bar: number };
type Baz = { baz: number };
const MyNestedUnion = unionize(
{
NestedFoo: ofType<Foo>(),
NestedBar: ofType<Bar>(),
NestedBaz: ofType<Baz>(),
},
{ value: "value" },
);
type MyNestedUnion = UnionOf<typeof MyNestedUnion>;
const MyUnion = unionize(
{
Foo: ofType<Foo>(),
Bar: ofType<Bar>(),
Baz: ofType<Baz>(),
Union: ofType<MyNestedUnion>(),
},
{ value: "value" },
);
// Hover this
type MyUnion = UnionOf<typeof MyUnion>;
// Hover this
declare const fn: (f: MyUnion) => void;
Now hover over type MyUnion and you'll see:
type MyUnion = ({
tag: "Foo";
} & {
value: Foo;
}) | ({
tag: "Bar";
} & {
value: Bar;
}) | ({
tag: "Baz";
} & {
value: Baz;
}) | ({
tag: "Union";
} & {
value: ({
tag: "NestedFoo";
} & {
value: Foo;
}) | ({
tag: "NestedBar";
} & {
...;
}) | ({
...;
} & {
...;
});
})
Observations:
-
{
tag: "Foo";
} & {
value: Foo;
}
… could be simplified to
{
tag: "Foo";
value: Foo;
}
Is this something TS should do automatically for us? In the interim, is this something we can workaround, e.g. using Compact (which "cleans" types with lots of arithmetic)?
- In the sans-Unionize example, the
MyNestedUnion type alias was shown nested inside of here. However in this Unionize example, the type alias MyNestedUnion is thrown away and the whole type structure is shown instead. Why? Is this a design limitation or bug in TS? Is it something we can workaround?
- Some types are truncated. This makes these types really difficult to work with. Often times, inspecting a type is not enough to understand how to work with it—instead you have to look deep into the definition. If we can't fix 2 (doing so would negate this), can we prevent truncation somehow?
Hover over const fn and you'll see:
const fn: (f: ({
tag: "Foo";
} & {
value: Foo;
}) | ({
tag: "Bar";
} & {
value: Bar;
}) | ({
tag: "Baz";
} & {
value: Baz;
}) | ({
tag: "Union";
} & {
value: ({
tag: "NestedFoo";
} & {
value: Foo;
}) | ({
tag: "NestedBar";
} & {
...;
}) | ({
...;
} & {
...;
});
})) => void
In the sans-Unionize example, the MyUnion type alias was shown here. However in this Unionize example, the type alias MyUnion is thrown away and the whole type structure is shown instead. Why?
I appreciate there might not be much we can do from Unionize's side to help address these UX issues. However I imagine there's at least some discussions inside of TS we could chime into, so our voice is heard. 🤞
We're using Unionize pretty heavily at Unsplash (thank you!). I would like to share some UX feedback relating to how the union types appear when inspected. To do so, I will use an example.
Vanilla tagged union
First let's see how type inspection works for a sans-Unionize union:
Hover over
type MyUnionand you'll see:Great! Hover over
const fnand you'll see:Great! No problems here.
Unionize union
Now if we define the equivalent tagged union but using Unionize:
Now hover over
type MyUnionand you'll see:Observations:
Compact(which "cleans" types with lots of arithmetic)?MyNestedUniontype alias was shown nested inside of here. However in this Unionize example, the type aliasMyNestedUnionis thrown away and the whole type structure is shown instead. Why? Is this a design limitation or bug in TS? Is it something we can workaround?Hover over
const fnand you'll see:In the sans-Unionize example, the
MyUniontype alias was shown here. However in this Unionize example, the type aliasMyUnionis thrown away and the whole type structure is shown instead. Why?I appreciate there might not be much we can do from Unionize's side to help address these UX issues. However I imagine there's at least some discussions inside of TS we could chime into, so our voice is heard. 🤞