Achieving this can be done through a combination of the following techniques:
interface Example {
a: string;
b: number;
}
type SingleProperty<T, K extends keyof T> = K extends any ? {[Prop in K]: T[Prop]} : never
type UnionOfProperties<T> = { [K in keyof T]: SingleProperty<T, K> }[keyof T];
type ExamplePropertiesUnion = UnionOfProperties<Example>
The expected result is as follows:
type ExamplePropertiesUnion = {
a: string;
} | {
b: number;
}
However, it's important to note that TypeScript allows the following scenario by default:
var t: ExamplePropertiesUnion = {a: "", b: 42}
This may not always align with our intended behavior, so for stricter type checking, we can use the following variant:
type FixTsUnion<T, K extends keyof T> = {[Prop in keyof T]?: Prop extends K ? T[Prop]: never}
type oneOf<T> = { [K in keyof T]: Pick<T, K> & FixTsUnion<T, K;}[keyof T];
// ok
var z1: oneOf<Example> = { a: "" };
// ok
var z2: oneOf<Example> = { b: 5 };
// error
var z3: oneOf<Example> = { a: "", b: 34 };
// error
var z4: oneOf<Example> = { };
Experiment with the code here
Related questions:
- TypeScript: How to map union type to another union type
- How to transform union type into intersection type