Here is a detailed example of a problem with some code:
type Mapper<T, R> = (data: T) => R;
interface Config<T, R> {
readonly mapper?: Mapper<T, R>;
}
function transform<T, R>(config: Config<T, R>, data: T) {
return config.mapper?.(data) ?? data;
}
const mapper: Mapper<number, string> = (data: number) => `${data};`
const result1 = transform({}, 1);
const result2 = transform({ mapper }, 1);
The question is how to properly type the result of the transform
function. Please note that the function transform
is a simplified example.
Now, let's consider the following code:
export type PendingState = {
type: "PENDING";
};
export type SuccessState<T> = {
type: "SUCCESS";
data: T;
};
export type ErrorState = {
type: "ERROR";
error: unknown;
};
export type SuspenseState<T> = PendingState | SuccessState<T> | ErrorState;
export type PendingStateMapper<R> = () => R;
export type SuccessStateMapper<T, R> = (data: T) => R;
export type ErrorStateMapper<R> = (error: unknown) => R;
export interface Mappers<T, P, S, E> {
readonly pendingState?: PendingStateMapper<P>;
readonly successState: SuccessStateMapper<T, S>;
readonly errorState: ErrorStateMapper<E>;
}
export function fromSuspenseState<T, P, S, E>(mappers: Mappers<T, P, S, E> & { pendingState: PendingStateMapper<P> }): OperatorFunction<SuspenseState<T>, P | S | E>;
export function fromSuspenseState<T, P, S, E>(mappers: Mappers<T, P, S, E> & { pendingState?: undefined }): OperatorFunction<SuspenseState<T>, null | S | E>;
export function fromSuspenseState<T, P, S, E>(mappers: Mappers<T, P, S, E>): OperatorFunction<SuspenseState<T>, P | S | E | null> {
return (source$: Observable<SuspenseState<T>>): Observable<P | S | E | null> => {
return source$.pipe(
map((state) => {
switch (state.type) {
case "PENDING":
return mappers.pendingState ? mappers.pendingState() : null;
case "SUCCESS":
return mappers.successState(state.data);
case "ERROR":
return mappers.errorState(state.error);
}
})
);
};
}
There is a solution using type inference based on property types. The code is provided to demonstrate the approach.
If you have faced similar situations or have suggestions, feel free to share your experience.