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;

Tags:

Typescript