0

I am looking for a way to group a set of partially applied functions with only the first parameter(s) being different. Or in other words; The group contains functions accepting any number of parameters of any type but the first application always has to return a function of type (state: State) => Event[].

For example a group of functions like this:

const group = { 
  func1: () => (state: State) => Event[],
  func2: (arg: string) => (state: State) => Event[],
  func3: (arg: string, arg2: number) => (state: State) => Event[],
}

All of these functions follow the pattern (not updated to accept multiple args):

export type Command = <T>(arg: T) => (state: State) => Partial<Event>[];

However, when I try to type the group like this:

const command: Record<string, Command> = {
  func1: () => (state: State) => [{}],
  func2: (arg: string) => (state: State) => [{}],
  func3: (arg: string, arg2: number) => (state: State) => [{}],
};

Typescript warns me that type T is not assignable to type string.

Type '(arg: string) => (state: State) => {}[]' is not assignable to type 'Command'.
  Types of parameters 'arg' and 'arg' are incompatible.
    Type 'T' is not assignable to type 'string'

I understand why it is not assignable but I can't figure out how I would then type this grouping of partially applied functions. I basically want to make sure that every function in this grouping follows the pattern of the type Command. That is to say, it should be a partially applied function with any parameters of any type that return a function of the type: (state: State) => Event[]

Is this possible, and if so, how would I do this?

1 Answers1

0

Is it helping to use any in your type declaration and type the function parameter later in your group like so?

export type Command = (...arg: any[]) => (state: State) => Partial<Event>[];

const command: Record<string, Command> = {
  func1: () => (state: State) => [{}],
  func2: (arg: string) => (state: State) => [{}],
  func3: (arg: string, arg2: number) => (state: State) => [{}],
};

Update

To be more specific with the parameters I would let Typescript infer the function signatures:

export type Command = (state: State) => Partial<Event>[];

const command = {
  func1: (): Command => (state: State) => [{}],
  func2: (arg: string): Command  => (state: State) => [{}],
  func3: (arg: string, arg2: number): Command => (state: State) => [{}],
};

command.func1() // OK
command.func1("test") // Err
command.func2() // Err
command.func2("test") // OK
command.func2([]) // Err
command.func3() // Err
command.func3("test", 2) // OK
command.func3([]) // Err

Or explicitly type the group:

export type Command = (state: State) => Partial<Event>[];
interface CommandsGroup {
    func1: () => Command;
    func2: (arg: string) => Command;
    func3: (arg: string, arg2: number) => Command
}

const command: CommandsGroup = {
  func1: () => (state: State) => [{}],
  func2: (arg: string) => (state: State) => [{}],
  func3: (arg: string, arg2: number) => (state: State) => [{}],
};

command.func1() // OK
command.func1("test") // Err
command.func2() // Err
command.func2("test") // OK
command.func2([]) // Err
command.func3() // Err
command.func3("test", 2) // OK
command.func3([]) // Err
Mue
  • 434
  • 5
  • 12
  • Interesting and thank you for your reply. When I try this out it causes all input arguments into the function to be correct according to typescript. In other words, typescript will not warn/catch me if I provide an incorrect type during developing. The type of command.func1() will always be: `(...arg: any[]): (state: State) => Event[]` So for func1 that expects a string, typescript won't catch me if I do: `command.func1(["val"]);` – Terry van Walen Jun 01 '21 at 12:58
  • You are right! That’s not very useful when looking at the parameter types. I’ve updated my answer a bit. – Mue Jun 02 '21 at 09:50
  • 1
    Thank you Mue, this is not exactly what I want but what I want is probably not possible and this is indeed the best approach. – Terry van Walen Jun 03 '21 at 13:09