#Typing function arguments based off array of argument types

1 messages · Page 1 of 1 (latest)

carmine delta
#

I need help completing this type:

interface ReturnTypes {
  string: string;
  number: number;
  boolean: boolean;
}

interface CommandOption {
    type: keyof ReturnTypes;
}

interface Command<Options extends CommandOption[] = CommandOption[]> {
  options?: Options;
  handler: (...args: ?) => void; // Complete the type.
}

const command = {
  options: [
    {
      type: "string",
    },
    {
      type: "number",
    },
    {
      type: "boolean",
    },
  ],
  handler(string, number, boolean) {
    
  },
} satisfies Command;

I know ReturnTypes would be used at some point, but not sure how to do the rest.

carmine delta
#

Dang I thought this would work

type OptionArguments<Options extends CommandOption[]> = Options extends [
  infer First extends CommandOption,
  ...infer Last extends CommandOption[]
]
  ? [ReturnTypes[First["type"]], ...OptionArguments<Last>]
  : never;
pallid oracle
#

I can't really work out what you are trying to do.

carmine delta
#

for example: ```ts
handler: (...args: [string, number, boolean]) => void;


but based off the types in "options"
pallid oracle
#

You mean if you set options as [{ type: string }, { type: number }] then the args would be [string, number] ?

carmine delta
#

yep

pallid oracle
#

So we probably just need a little mapped type

carmine delta
#

I just noticed I called them "ReturnTypes" but they are not return types. Should have been "ArgumentTypes" lol

pallid oracle
#

wait a min

#

OK I think that is working, but you did some really weird things, so let's unpick those

#
interface ArgumentTypes {
  string: string;
  number: number;
  boolean: boolean;
}

interface CommandOption {
    type: keyof ArgumentTypes;
}
#

The key ArgumentTypes is a string literal 'string' 'number' 'boolean'

#

I am guess you don't want a string literal, but rather a type?

carmine delta
#

Yeah I want the type to be a string, as its coming from an API

pallid oracle
#

hmm, but the type is written as a string

#

so we need to convert from a type name string to a type

#
handler(a: string, b: number, c: boolean) {

}

And this would be a valid handler?

carmine delta
#

Yea

pallid oracle
#

Somewhere this is a thing to convert a string to a type, but I can't find it

#

Can we use something brute forcy for this?

#
type StringToType<T extends string> = T extends 'number' ? number : T extends 'boolean' ? boolean : never;

like this?

carmine delta
#

Doesnt the interface do the same thing?

pallid oracle
#

hmm does it

#

yeah I suppose so, I get what you're doing now 😄

winged fractalBOT
#
sandiford#0

Preview:```ts
interface ArgumentTypes {
string: string
number: number
boolean: boolean
}

type ArgumentType = keyof ArgumentTypes

interface CommandOption {
type: ArgumentType
}

type ArgsFromOptions<
Options extends readonly CommandOption[]

= {
[K in keyof Options]: ArgumentTypes[Options[K]["type"]]
...```

pallid oracle
#

I think it won't work with satisfies

carmine delta
#
function defineCommand<const T extends Command>(command: T) {
  return command;
}
#

this should force everything to be const

pallid oracle
#

OK

carmine delta
#

oh you did something like that

pallid oracle
#

Well, I was just trying that. Your approach seems best.

#

the readonly's are not necessary, they don't seem to change anything

winged fractalBOT
#
sandiford#0

Preview:```ts
interface ArgumentTypes {
string: string;
number: number;
boolean: boolean;
}

type ArgumentType = keyof ArgumentTypes

interface CommandOption {
type: ArgumentType;
}

type ArgsFromOptions<Options extends CommandOption[]> = {
[K in keyof Options]: ArgumentTypes[Options[K]['type']]
...```

pallid oracle
#
interface Command<Options extends CommandOption[] = CommandOption[]> {

I suspect that the default type here isn't useful. It doesn't match any real defined command, and not sure how else it can be useful.

#

So I'd probably remove that, to remind me that I need to specify an Options type

carmine delta
#

I definitely tried your solution but only with satisfies, so probably thats why it didnt work.

carmine delta
#

wonder why you have to specify const Options extends CommandOptions[] and not const T extends Command

pallid oracle
#

Command is Command<CommandOption[]>

#

When you use a Generic type you need to provide the type argument for it, or else let it use its default

#

Which is part of why I say you should remove the default type for it, as you think you're using it Generically, when actually you are using it as Command<CommandOption[]>, and Command<CommandOption[]> is basically useless

#
// no type errors, but nonsensical
const commandWithSatisfies = {
  options: [
    {
      type: "string",
    },
    {
      type: "number",
    },
    {
      type: "boolean",
    },
  ] as const,
  handler(
    a: ArgumentTypes[ArgumentType],
    b: ArgumentTypes[ArgumentType],
    c: ArgumentTypes[ArgumentType],
    d: ArgumentTypes[ArgumentType]
  ) {
  },
} satisfies Command;

to satisfy that constraint you need to use string | number | boolean for each arg, and you can do weird things like provide more arguements than entries in your options.

#

And T extends Command<CommandOption[]> is not the same as Command<T extends CommandOption[]> of course