Suppose I have a type such as
interface Definition {
[key: string]: {
optional: boolean;
}
}
Is it now possible to construct a type ValueType<T extends Definition> that, for a definition
{
foo: { optional: true },
bar: { optional: false }
}
outputs the type
{
foo?: string;
bar: string;
}
Is that possible?
My first approach would have been a construct made of a mapped type and Omit which works by merging the optional and required properties into a single object. However, this requires a union type made of all the optional keys and I am at a loss on how to do that.
The basic Idea was:
type ValueType<T extends Definition> =
{ [key in keyof T]?: string }
& Omit<{ [key in keyof T]: string }, OptionalKeys<T>>
With OptionalKeys<T> returning the aforementioned union type made from all of the optional keys.
You can get that behaviour with this logic:
type ValueType<T extends Record<string, { optional: boolean }>> =
(
{ [K in keyof T]?: string } &
{ [K in keyof T as T[K]["optional"] extends false ? K : never]-?: string }
) extends infer O ? { [K in keyof O]: O[K] } : never
We create an intersection of all keys where optional is true with ? and all keys where optional is false. The extends infer O ? {[K in keyof O]: O[K]} : never at the end makes the type "prettier" but is functionally irrelevant.
Edit:
Thanks to @jcalz. We can make it a bit shorter and also keep the order of the keys.
Playground
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