Given a type that contains all possibilities (e.g. non-tagged union):
type Input = {
a: string,
b: number,
c: boolean,
};
I want to map it with this kind of API:
type MapItSomehow<T> = ???;
type Output = MapItSomehow<Input>;
And ultimately get this:
type Output = {
a: string,
b: undefined,
c: undefined,
} | {
a: undefined,
b: number,
c: undefined,
} | {
a: undefined,
b: undefined,
c: boolean,
};
The proof that this works is if I can do:
let r: Result = ([] as Result[])[0];
if ('a' in r) {
r.a.trim() // not r.a!.trim()
}
else if // same for each field ...
CodePudding user response:
It is possible to do with mapped types, Record
and Exclude
:
type Input = {
a: string,
b: number,
c: boolean,
};
type Values<T> = T[keyof T]
type Mapped<T> = Values<{
[Prop in keyof T]: Record<Prop, T[Prop]>
}>
type Result = Mapped<Input>
declare let x: Result
if ('a' in x) {
x.a.trim()
}
Mapped
- iterates through each property and creates new part of expected union type where current key is set and other keys Exclude<keyof T, Prop>
are undefined
Values
- obtains a union type of all object values