I need a RxJS operator that can check that if a property on an object exists, and then change the type to correctly reflect this.
I already have an operator that will do this for an emitted value:
const notUndefined = <T>() =>
filter<T | undefined, T>(
(x): x is T extends undefined ? never : T => x !== undefined
);
But I also need to be able to do it for one of its properties that can be used something like this:
interface MyInterface {
myKey?: string;
}
of<MyInterface>({ myKey: "Some value" }).pipe(
notUndefinedKey("myKey"),
map((x) => x.myKey)
// ^? (property) myKey: string
)
of<MyInterface>({ myKey: "Some value" }).pipe(
map((x) => x.myKey)
// ^? (property) MyInterface.myKey?: string | undefined
)
I've got a first working version, but it seems a bit excessive and seems to also remove methods from objects:
import { OperatorFunction, filter, of, map } from "rxjs";
// Typescript types helper
type DefinedFields<TType> = {
[TKey in keyof TType]-?: DefinedFields<NonNullable<TType[TKey]>>;
};
type WithDefinedKey<TType, TKey extends keyof TType> = Omit<TType, TKey> & DefinedFields<Pick<TType, TKey>>;
type OperatorToDefinedKey<TType, TKey extends keyof TType> = OperatorFunction<
TType | undefined,
WithDefinedKey<TType, TKey>
>;
// Operator
export const notUndefinedKey = <TType, TKey extends keyof TType>(
key: TKey
): OperatorToDefinedKey<TType, TKey> =>
filter<TType>((x) => x?.[key] !== undefined) as OperatorToDefinedKey<
TType,
TKey
>;
Is there a nice way to do this? One that maybe also keeps more of the original interface like methods on an object?