By utilizing the defined types showcased in this link, we can construct a conditional type where if a field is required, the type of the field will be either { required : true }
or {}
based on the condition:
type DomainDefinition<F, M> = {
fields?: {
[K in keyof F]: ({} extends { [P in K]: F[K] } ? {} : { required: true }) & {} // Intersection with other properties as needed
},
methods?: { [K in keyof M]: M[K] & Function },
}
type User = {
id: string,
name?: string
}
function createDomain<T>(o: DomainDefinition<T, any>) {
return o;
}
export const User = createDomain<User>({
fields: {
id: { required: true },
name: {},
},
});
Please note: This approach tests for optionality (using the ?
modifier) and not for nullability (| null | undefined
). Depending on your specific requirement, this distinction may or may not be significant.
You might also find the following answer interesting, which includes a test for the readonly
modifier. It introduces an additional isReadonly
field:
type IfEquals<X, Y, A, B> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? A : B;
type DomainDefinition<F, M> = {
fields?: {
[K in keyof F]:
({} extends { [P in K]: F[P] } ? {} : { required: true })
& IfEquals<{ [P in K]: F[P] }, { -readonly [P in K]: F[P] }, {}, { isReadonly: true }>
},
methods?: { [K in keyof M]: M[K] & Function },
}
type User = {
id: string,
readonly name?: string
}
function createDomain<T>(o: DomainDefinition<T, any>) {
return o;
}
export const User = createDomain<User>({
fields: {
id: { required: true },
name: { isReadonly: true },
},
});
If it's necessary to filter out certain properties, such as functions, you would need to replace all instances of F
with the filtered F
. To simplify this process, defining an additional type alias can help:
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];
type DomainPropertyHelper<F> = {
[K in keyof F]: ({} extends { [P in K]: F[K] } ? {} : { required: true }) & {} // Intersection with other properties as needed
};
type DomainDefinition<F, M> = {
fields?: DomainPropertyHelper<Pick<F, NonFunctionPropertyNames<F>>>,
methods?: { [K in keyof M]: M[K] & Function },
}