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.
CodePudding user response:
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.