I've got a simple api call function that fetches data and maps the result conditionally based on the arguments flags.
As a fetch result I'm getting a conditional type or a union type, but I'm not able to use type guards here because these two types don't share a common discriminated property and I'm supposed to discriminate them based on external type. How can I tell typescript that these two types depend on another type? Is this the case to add another layer of indirection?
// prerequisites
declare function apiService(...a: any[]): any;
declare function mapExternalFoo(x: IExternalFoo): IFoo;
declare function mapExternalBar(x: IExternalBar): IBar;
interface IFoo { a: any; }
interface IBar { b: any; }
interface IExternalFoo { c: any; }
interface IExternalBar { d: any; }
interface IProps<T extends boolean> {
isExtended?: T;
}
// I want it to look like this, but it fails with a compile error
function fetchData<T extends boolean>(params: IProps<T>): Promise<T extends true ? IFoo : IBar> {
return apiService('/user/data', params).then(
(data: T extends true ? IExternalFoo[] : IExternalBar[]) =>
// Cannot invoke an expression whose type lacks a call signature.
params.isExtended ? data.map(mapExternalFoo) : data.map(mapExternalBar)
);
}
I was able to get this to work with type casting and to get the correct return types with function calls but it's cumbersome and I feel that's not the right way to do it
function fetchData(params: IProps<true>): Promise<IFoo[]>;
function fetchData(params: IProps<false>): Promise<IBar[]>;
function fetchData<T extends boolean>(params: IProps<T>) {
return apiService('/user/data', params)
.then(
(data: IExternalFoo[] | IExternalBar[]) =>
params.isExtended
? (data as IExternalFoo[]).map(mapExternalFoo)
: (data as IExternalBar[]).map(mapExternalBar)
);
}
Adding a type alias for the conditional type helps to get rid of the lack of call signature error, but I'm getting mismatched types error instead
type INarrow<T extends boolean> = T extends true ? IExternalFoo[] : IExternalBar[];