spearkit
Guides

Options

Slash command options are declared as a map of name → builder. spearkit infers the exact value type each option resolves to, so your handler's ctx.options is fully typed — no…

import { command, option } from "spearkit";

command({
  name: "profile",
  description: "Show a profile",
  options: {
    user: option.user({ description: "Whose profile", required: true }),
    detailed: option.boolean({ description: "Show extra detail" }),
  },
  run: (ctx) => {
    ctx.options.user;     // User
    ctx.options.detailed; // boolean | undefined
  },
});

Builders and resolved types

BuilderResolved typeType-specific config
option.string(config)stringchoices, minLength, maxLength, autocomplete
option.integer(config)numberchoices, minValue, maxValue, autocomplete
option.number(config)numberchoices, minValue, maxValue, autocomplete
option.boolean(config)boolean
option.user(config)User
option.channel(config)channel unionchannelTypes
option.role(config)Role | APIRole
option.mentionable(config)user / role / member
option.attachment(config)Attachment

Every builder accepts the common config:

{
  description: string;                        // required
  required?: boolean;                         // default: false
  nameLocalizations?: LocalizationMap;
  descriptionLocalizations?: LocalizationMap;
}

Inference rules

spearkit narrows the resolved type from your declaration:

options: {
  // required → the value type, never undefined
  name: option.string({ description: "Name", required: true }),     // string

  // optional (default) → value | undefined
  age: option.integer({ description: "Age" }),                      // number | undefined

  // choices → a literal union of the choice values
  size: option.string({
    description: "Size",
    choices: [
      { name: "Small", value: "sm" },
      { name: "Large", value: "lg" },
    ],
  }),                                                               // "sm" | "lg" | undefined
}
  • Required options resolve to the value type.
  • Optional options resolve to value | undefined (spearkit converts discord's absent value to undefined, never null).
  • choices narrow string/integer/number options to a literal union of the declared values.
run: (ctx) => {
  const name: string = ctx.options.name;
  const age: number | undefined = ctx.options.age;
  const size: "sm" | "lg" | undefined = ctx.options.size;
};

Numeric and length constraints

options: {
  count: option.integer({ description: "How many", minValue: 1, maxValue: 100 }),
  code: option.string({ description: "Code", minLength: 4, maxLength: 8 }),
}

Channel options

Restrict the selectable channel types with channelTypes (from discord.js ChannelType):

import { option, ChannelType } from "spearkit";

options: {
  target: option.channel({
    description: "A text or announcement channel",
    channelTypes: [ChannelType.GuildText, ChannelType.GuildAnnouncement],
  }),
}

Choices

choices are { name, value } pairs. name is shown to the user; value is what your handler receives (and what spearkit narrows the type to).

option.integer({
  description: "Priority",
  choices: [
    { name: "Low", value: 1 },
    { name: "High", value: 2 },
  ],
  // optional per-choice localization:
  // choices: [{ name: "Low", value: 1, nameLocalizations: { tr: "Düşük" } }],
}); // 1 | 2 | undefined

Autocomplete

Provide an autocomplete handler instead of fixed choices to suggest values as the user types. spearkit marks the option as autocompletable, routes the autocomplete interaction, and (for subcommands) finds the right option.

const fruits = ["apple", "apricot", "banana", "cherry"];

option.string({
  description: "Fruit",
  required: true,
  autocomplete: (ctx) =>
    fruits
      .filter((f) => f.startsWith(ctx.value))
      .map((f) => ({ name: f, value: f })),
});

The autocomplete handler receives an AutocompleteContext:

MemberDescription
ctx.valueThe current partial value typed by the user.
ctx.focusedNameThe name of the option being completed.
ctx.commandNameThe command being completed.
ctx.client / ctx.user / ctx.guild / ctx.guildIdAccessors.
ctx.respond(choices)Send suggestions (capped at discord's 25).

Returning the choices array (as above) is enough — spearkit calls respond for you. Returning [] shows no suggestions.

See also

  • Commands — using options inside commands and subcommands.
  • Components — buttons, selects and modals.

On this page