#Function return type based on property values from input

1 messages · Page 1 of 1 (latest)

hidden echo
#

I have a function that wraps another function that returns type any that I cannot change (it's a library).
I would like to be able to return a type in my wrapper function based on properties given as input in a config object.

e.g.

export interface Config<T> {
 options: T[]; 
 multiple: boolean;
 extra?: SomeExtraConfig;
}

List of interfaces it should return based on the above interface ^

// if `multiple: true`
{
 selection: T[];
}
// if `multiple: false`
{
 selection: T;
}
// if `extra` defined`
{
 selection: T | T[]; // selection type based on `multiple`
 extraData: SomeInterface;
}

Maybe I can just do conditional if statements to assert different return types, but wondering if there is any nifty tricks I can employ.

hidden echo
#

Right, and just create different interfaces for the different overloads? (for input and output).
That'll probably work. Could get a bit messy. I think in my case, that'd require 4-5 input interfaces, but probably more the fault of the function implementation itself.

#

I don't think it's possible, due to the overlap in the types with the overload.

export interface BaseConfig<T> {
  options: T[];
  extra?: SomeExtraConfig;
}

export interface MultiConfig<T> extends BaseConfig<T> {
  multiple: true;
}

export interface SingleConfig<T> extends BaseConfig<T> {
  multiple: false;
}

export type Config<T> = SingleConfig<T> | MultiConfig<T>
#

Would probably work if multiple was an optional, but that doesn't really make sense.

#

I'm trying to do something like this, but can't seem to get it working:

public doFunc<L, T extends Config<L>, K extends T['multiple']>(config: T) {
    return someCalculatedData as (K extends true ? Observable<L[]> : Observable<L>);
}
real yew
#

i think you could achieve it with the function being generic

winter vapor
#

i think this is overkill, just use overloads

#
function f<T>(multiple: false): SingleConfig<T, never>;
function f<T>(multiple: true): MultipleConfig<T, never>;
function f<T>(multiple: boolean): SingleConfig<T, never> | MultipleConfig<T, never>;
function f<T, U>(multiple: false, extra: SomeExtraConfig<U>): SingleConfig<T, U>;
function f<T, U>(multiple: true, extra: SomeExtraConfig<U>): MultipleConfig<T, U>;
function f<T, U>(multiple: boolean, extra: SomeExtraConfig<U>): SingleConfig<T, U> | MultipleConfig<T, U>;
hasty vortex
#

i think realistically if this is application code you should go for the simpler route via funcForSingleConfig and funcForMultiConfig, alternatively, does it need a config?

#

like what n_n suggested, could just be func<T, E>(multiple: true | false, options: T[], extra?: E) and then use some inference

#
export interface Config<TOpt> {
 multiple: true | false;
 options: TOpt[]; 
}

declare function handle<T, TConf extends Config<T>, TExtra>(config: TConf, extra?: TExtra): TConf extends { multiple: infer TMult } ?
  TMult extends true ? Observable<TConf["options"][number]>
  : Observable<TConf["options"]>
  : never

 handle({ multiple: true, options: [1,2,3] }) // Observable<number>
 handle({ multiple: false, options: [1,2,3] }) // Observable<number[]>
#

that fits ur suitcase tho methinks