I am in need of a customized type to depict an object where the properties can optionally be RxJS Observables.
The most straightforward approach to accomplish this is by using the following type:
type OptionalObservables<T> = {
[K in keyof T]: T[K] | Observable<T[K]>
}
You can use it as follows:
const animal: OptionalObservables<Animal> = {
species: 'dog',
noise: of('bark') // RxJS Observable that satisfies Observable<string>
}
(I have a separate function for parsing an object and subscribing to observables. However, this query does not focus on the RxJS aspect.)
But... I typically favor utilizing the $
suffix convention specifically for observables. Thus, I prefer adding a $ at the end when selecting an observable key. This kind of manipulation is feasible with Key Remapping.
{
species: 'dog',
noise$: of('bark')
}
Aligning everything together turned out to be more challenging than expected!
The final type required for
OptionalObservables<{ species: string, noise: string }>
would resemble:
// species either as a string or observable
({ species: string } | { species$: Observable<string> }) &
// noise either as a string or observable
({ noise: string } | { noise$: Observable<string> })
(Each property K of T must be mandatory and could be of type T[K] or Observable<T[K]>).
The closest foundational concept I reached was something like this:
type OptionalObservables<T> =
{
[K in keyof T]: K extends string ?
// original property name and type
{ [P in K]: T[K] }
|
// OR original property name with $ suffix and Observable type
{ [P in `${K}\$`]: Observable<T[K]> }
: never
}
This introduces an added level of 'nesting' (yet meets my criteria) resulting in:
{
species: {
species: "cat" | "dog";
} |
{
species$: Observable<"cat" | "dog">;
};
noise: {
noise: "bark" | "meow";
} |
{
noise$: Observable<"bark" | "meow">;
};
}
I had hoped that reaching this point would enable me to utilize something like Unionize to extract the values and merge them back without the extra nesting. So far, I've only succeeded in making all properties required or all optional!
If there's a technique I'm overlooking or if it's unattainable - I am eager to make this work.
An alternative approach might involve validating the structure's compatibility through a method rather than solely relying on a mapped type.