I am wondering what's available in typescript and mapped types to create a mapped type that will apply readonly and optional properties as defined in the initial type. Example will be easier to explain.
I've made an example as simple as possible, starting with a type defined as
type PropertyAttributes = {
readonly optional?: boolean;
readonly readonly?: boolean;
readonly value: string; //this would be something else but to simplify example like this
};
type ObjectDefinition = Record<string, PropertyAttributes>
This is the template in a way, from which I would like to create a type. For now I have this solution which works quite well by creating 4 different types and more less combining them.
//Four types corresponding to 4 types of objects
type ReadonlyRequired<O extends ObjectDefinition> = {
+readonly [K in keyof O as O[K]["readonly"] extends true
? O[K]["optional"] extends true
? never
: K
: K]: O[K];
};
type ReadonlyOptional<O extends ObjectDefinition> = {
+readonly [K in keyof O as O[K]["readonly"] extends true
? O[K]["optional"] extends true
? K
: never
: never]?: O[K] | undefined;
};
type MutableRequired<O extends ObjectDefinition> = {
-readonly [K in keyof O as O[K]["readonly"] extends true
? never
: O[K]["optional"] extends true
? never
: K]: O[K];
};
type MutableOptional<O extends ObjectDefinition> = {
-readonly [K in keyof O as O[K]["readonly"] extends true
? never
: O[K]["optional"] extends true
? K
: never]?: O[K] | undefined;
};
//Expand Util
export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
//Put them all together
type Combine<O extends ObjectDefinition> = Expand<
ReadonlyRequired<O> &
ReadonlyOptional<O> &
MutableRequired<O> &
MutableOptional<O>
>;
Then finally a type that I think is not relevant to the core of the question but it maps the resulting type to take the object
//simple maps the Object to value preserving keys since homomorphic as I understand
type MapToValue<O extends ObjectDefinition> = {[K in keyof O]: NonNullable<O[K]>["value"]} //need NonNullable for optional types or infered as unknown
type ToObj<O extends ObjectDefinition>=MapToValue<Combine<O>>
This works fine, it's a bit long and the only issue is the order is not preserved because we have to split it out. I'm just wondering if anyone can think of a different way?
Or any even minor improvements you can think of would be awesome.
Here is playground link typescript playground
Thank you