Reverse required and optional properties
For your question as stated (where Foo
has no index signature), you can define Flip
(which I'll call FlipOptional
) like this:
type OptionalKeys<T> = {
[K in keyof T]-?: {} extends Pick<T, K> ? K : never
}[keyof T];
type FlipOptional<T> = (Required<Pick<T, OptionalKeys<T>>> &
Partial<Omit<T, OptionalKeys<T>>>) extends infer O
? { [K in keyof O]: O[K] }
: never;
First we have to determine which keys of T
are optional. There's no simple built-in way to do this, but the above OptionalKeys
type alias above works by checking which single-property widenings of T
are weak types, as described in answer to a related question. Index signatures mess this up, so if you need to operate on types with an index signature, you'll need something more complicated (which is also described in the linked answer).
Once you can figure out which keys are optional, it's relatively straightforward to construct an equivalent type to what you want, with Pick
and Omit
along with Partial
and Required
and an intersection: Required<Pick<T, OptionalKeys<T>>> & Partial<Omit<T, OptionalKeys<T>>>
. The only problem is that this type is relatively ugly; it would be nicer if the type were written out as a single object type.
Here I use a trick with conditional type inference of the following form. Say we have a type Ugly
which consists of a lot of intersections and mappings. To make it pretty, we can do
type Pretty = Ugly extends infer O ? {[K in keyof O]: O[K]} : never
Here, Ugly
is essentially "copied" to the O
parameter, which is mapped over without changing anything. This is mostly a no-op in terms of the output, but the result will be a single object type if possible.
Let's try it out:
interface Foo {
a?: string;
b: string;
}
type Bar = FlipOptional<Foo>;
/* type Bar = {
a: string;
b?: string | undefined;
} */
type FooAgain = FlipOptional<Bar>;
/* type FooAgain = {
b: string;
a?: string | undefined;
} */
type MutuallyAssignable<T extends U, U extends V, V = T> = true;
type FooAgainIsFoo = MutuallyAssignable<Foo, FooAgain>; // okay
This all looks good. Bar
is what you expect, and FlipOptional<Bar>
gives a type equivalent to Foo
.
Okay, hope that helps. Good luck!
Link to code
You can use a type system like this to construct Flip<T>
:
type OptionalPropertyNames<T> = {
[K in keyof T]-?: undefined extends T[K] ? K : never
}[keyof T];
type RequiredPropertyNames<T> = {
[K in keyof T]-?: undefined extends T[K] ? never : K
}[keyof T];
type OptionalProperties<T> = Pick<T, OptionalPropertyNames<T>>
type RequiredProperties<T> = Pick<T, RequiredPropertyNames<T>>
type Flip<T> = Partial<RequiredProperties<T>> & Required<OptionalProperties<T>>;
type Bar = Flip<X>;
// ==
interface Bar {
a: string;
b?: string | undefined;
}
See this playground.
If you are ok with having Flip
produce a type in which no property is optional, but instead the value as union with undefined
you can it define like this:
type Flip<X> = {
[K in keyof X]-?: undefined extends X[K] ? X[K] : X[K] | undefined;
};
Which produces the following type in your example:
type Bar = Flip<Foo>;
// Equivalent to
interface Bar {
a: string;
b: string | undefined;
}
Which should be sufficient in most cases and provides better autocompletion and "cleaner" types then the example above.
Explanation:
[K in keyof X]-?:
this makes every property required. The next bit (conditional types) checks if the type was required and creates a union with undefined
in that case - making it effectively optional:
X[K] extends undefined ? X[K] : X[K] | undefined;