Home > Software engineering >  Omit empty array from generic function paramater call in typescript
Omit empty array from generic function paramater call in typescript

Time:10-13

I am looking for a way to not provide an empty array for a generic function Parameter<F>-typed parameter when F does not receive parameters. The following working example shows the current state

type A<F extends (...args: any[]) => any> = {
    shouldPrintHello: boolean;
    params: Parameters<F>;
};

const wrappingFunction = <F extends (...args: any[]) => any>(sentFunction: F, defaultParams: A<F>) => {
    const innterFunction = (...args: Parameters<F>) => {
        if (defaultParams.shouldPrintHello) console.log("hello");
        sentFunction(args);
        return;
    };

    const defaultValue = sentFunction(defaultParams);
    return innterFunction;
};

const f1 = wrappingFunction(
    (arg0: string) => {
        return;
    },
    { shouldPrintHello: true, params: ["defaultString"] }
);

const f2 = wrappingFunction(
    () => {
        return;
    },
    { shouldPrintHello: true, params: [] }
);

f1("a string");
f2();

Desired (pseudo) code changes:

type A<F extends (...args: any[]) => any> = {
    shouldPrintHello: boolean;
    params: Parameters<F> === [] ? undefined : Parameters<F>;
};

const f2 = wrappingFunction(
    () => {
        return;
    },
    { shouldPrintHello: true }
);

f2();

CodePudding user response:

Don't be afraid to use extends to check for "equality":

type A<F extends (...args: any[]) => any> = {
    shouldPrintHello: boolean;
    params: Parameters<F> extends [] ? undefined : Parameters<F>;
};

Alternatively, you can check the length:

params: Parameters<F>["length"] extends 0 ? undefined : Parameters<F>;

CodePudding user response:

There's no built-in way to express that a property is optional if and only if some condition holds of its value, so if you want a type like that you have to write it yourself as a conditional type:

type OptionalParamsIfAllowedToBeEmpty<P> =
  [] extends P ? { params?: P } : { params: P }

And then you can intersect it into the rest of your type definition:

type A<F extends (...args: any[]) => any> = {
  shouldPrintHello: boolean;
} & OptionalParamsIfAllowedToBeEmpty<Parameters<F>>;

declare const wrappingFunction: <F extends (...args: any[]) => any>(
  sentFunction: F,
  defaultParams: A<F>) => (...args: Parameters<F>) => void

Let's test it out. First, let's make sure it behaves as desired for functions with at least one required parameter:

wrappingFunction(
  (arg0: string) => {
    return;
  },
  { shouldPrintHello: true, params: ["defaultString"] }
);    

wrappingFunction(
  (arg0: string) => {
    return;
  },
  { shouldPrintHello: true } // error! missing params
);

Looks good, params is not optional. Then let's test it for functions without any parameters:

wrappingFunction(
  () => {
    return;
  },
  { shouldPrintHello: true }
);

wrappingFunction(
  () => {
    return;
  },
  { shouldPrintHello: true, params: [] } // okay
);

wrappingFunction(
  () => {
    return;
  },
  { shouldPrintHello: true, params: undefined } // okay
);

wrappingFunction(
  () => {
    return;
  },
  { shouldPrintHello: true, params: ["defaultString"] } // error
);

Also looks good; you can leave out params, or pass in undefined or an empty array [], but it won't let you pass in the wrong parameters. Finally, let's see how it behaves with a function with parameters that are all optional:

function f(x?: string) { }
wrappingFunction(f, { shouldPrintHello: false }) // okay
wrappingFunction(f, { shouldPrintHello: false, params: ["abc"] }) // okay

That's also what you want, I think. Since you can call f(), you shouldn't be required to pass in params in wrappingFunction, but since you can also call f("abc"), you should be allowed to pass in params as ["abc"]. This is an important difference between the [] extends P check in OptionalParamsIfAllowedToBeEmpty<P> and a seemingly similar P extends [] check; if P is [x?: string], then [] extends [x?: string] is true, but [x?: string] extends [] is false.

Playground link to code

  • Related