Let’s say I have the following type:
interface Foo {
a?: string;
b: string;
}
All properties can be made required using Required
.
type Bar = Required<Foo>;
// Equivalent to
interface Bar {
a: string;
b: string;
}
All properties can be made optional using Partial
.
type Bar = Partial<Foo>;
// Equivalent to
interface Bar {
a?: string;
b?: string;
}
I would like to make a generic type than flips the optional flag for all properties.
type Bar = Flip<Foo>;
// Equivalent to
interface Bar {
a: string;
b?: string;
}
The implementations for Partial
and Required
are pretty straightforward, but this Flip
type would need to know which properties of Foo
are optional and which are required. Is is possible to read this modified using a generic?
Some context: I believe this would be a useful type for defaultProps
in React.
Optional Properties are those properties that are not mandatory. In the example below the person object name is usually mandatory but age may not be.
First, if you don't tell TypeScript that a property is optional, it will expect it to be set. Adding ? to the property name on a type, interface, or class definition will mark that property as optional. type Foo = { bar?: number; } const a: Foo = {}; // This is now OK!
Optional property: In Typescript you can declare a property in your interface which will be optional. Suppose you have a interface for employee and middle name is optional then your code will look like this: interface IEmployee { firstName: string; lastName: string; middleName?: string; }
The declaration of the interface is a place to make some properties optional. You can achieve that with a question mark next to the properties names. In the above example, the pet is an optional property.
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With