'Create C-Style Union in TS Type System
I am trying to create a generic type that allows me to type what a C union value could look like if it was in TypeScript. For example, I want to turn this type:
type UserIdentifier = {
id: string;
dateCreated: string;
names: {
first: string;
last: string;
}
};
Into this:
type UserIdentifier = {
id: string;
dateCreated?: undefined;
names?: undefined;
} | {
id?: undefined;
dateCreated: string;
names?: undefined;
} | {
id?: undefined;
dateCreated?: undefined;
names: {
first: string;
last: string;
};
};
The reason I need the other properties explicitly set to undefined and optional is because TypeScript doesn't seem to complain if you have extra properties in one end of the union that are not present in the declaration. For example, this code does not error:
const f: { first: string; } | { last: string; } = { first: "hey", last: "you" };
Solution 1:[1]
You can do this via mapped types and generics. Basically, I iterate through the keys of T then construct an object with the given key and it's respective value and join it with another object that includes all of the other keys except the current one assigned to undefined and optional. Here is my solution:
type CUnion<T extends Record<PropertyKey, unknown>>
= { [K in keyof T]: { [_ in K]: T[K] } & { [_ in Exclude<keyof T, K>]?: undefined } }[keyof T];
type UserIdentifier = CUnion<{
id: string;
dateCreated: string;
names: {
first: string;
last: string;
}
}>;
function testIds(...args: UserIdentifier[]) {}
testIds(
{ id: "hey" }, // correctly passes
{ dateCreated: "hey" }, // correctly passes
{ names: { first: "hey", last: "you" } }, // correctly passes
);
const fail1: UserIdentifier = { names: { first: "", last: "" }, id: "" } // correctly fails
const fail2: UserIdentifier = { id: "hey", dateCreated: "hey" } // correctly fails
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
