# Options

Declare options on a slash command and read them back through getters typed from what you declared. Covers the option kinds, required options dropping the null, choices, channel types, and a getter that goes missing.

Every option you declare becomes a getter on [`this.options`](https://docs.seedcord.org/packages/gateway/latest/slash-handler#options), typed from what you declared. Let's take this command as an example.

```ts title="src/commands/Maintenance.ts"
import {
    BuilderComponent,
    RegisterCommand
} from '@seedcord/gateway';
import { ChannelType } from 'discord.js';

@RegisterCommand('global')
export class Maintenance extends BuilderComponent<'command'> {
    constructor() {
        super('command');

        this.instance
            .setName('maintenance')
            .setDescription('Post a maintenance notice')
            .addUserOption((option) =>
                option
                    .setName('notify')
                    .setDescription('Member to notify')
                    .setRequired(true)
            )
            .addChannelOption((option) =>
                option
                    .setName('target')
                    .setDescription('Channel to post in')
                    .setRequired(true)
                    .addChannelTypes(
                        ChannelType.GuildText,
                        ChannelType.GuildAnnouncement
                    )
            )
            .addStringOption((option) =>
                option.setName('reason').setDescription('Why')
            );
    }
}
```

The handler reads `notify`, `target`, and `reason` back by name.

```ts title="src/handlers/Maintenance.ts"
@SlashRoute('maintenance')
export class Maintenance extends SlashHandler<'maintenance'> {
    public async execute(): Promise<void> {
        const notify = this.options.getUser('notify');
        const target = this.options.getChannel('target');
        const reason = this.options.getString('reason');

        await target.send(`Maintenance incoming. ${reason ?? ''}`);
        await this.reply(`Notified ${notify.username}.`);
    }
}
```

Run `seedcord codegen` after you add or change an option, since the handler keeps the old shape until you do.

## The kinds

Each getter only accepts the names that command declared for its own kind. A typo is a compile error.

`maintenance` declares a user, a channel, and a string. That gives it four getters, since a user option answers both `getUser` and `getMember`.

```ts
this.options.
```

Each one then offers that command's options of its own kind. `maintenance` declared one string, so `getString` lists `reason`.

```ts
this.options.getString('
```

## Required options drop the null

`notify` and `target` were declared with `setRequired(true)`, so they arrive as values. `reason` was left optional, so it arrives as `string | null`. Discord rejects the command before your handler runs when a required option is missing.

Mark an option required when your handler has nothing sensible to do without it. Discord then turns the caller away at the picker, where they can still fix it, and your handler reads the value with no guard.

```ts
const notify = this.options.getUser('notify');
const reason = this.options.getString('reason');
```

{/* prettier-ignore-start */}

| you declare            | you call         | you get                   |
| ---------------------- | ---------------- | ------------------------- |
| `addStringOption`      | `getString`      | `string`                  |
| `addIntegerOption`     | `getInteger`     | `number`, whole           |
| `addNumberOption`      | `getNumber`      | `number`, decimal allowed |
| `addBooleanOption`     | `getBoolean`     | `boolean`                 |
|                        |                  |                           |
| `addUserOption`        | `getUser`        | the user                  |
| `addUserOption`        | `getMember`      | the member, or `null`     |
| `addChannelOption`     | `getChannel`     | the channel               |
| `addRoleOption`        | `getRole`        | the role                  |
| `addMentionableOption` | `getMentionable` | a user, member, or role   |
| `addAttachmentOption`  | `getAttachment`  | the attachment            |

{/* prettier-ignore-end */}

> **Gateway and http differ**
>
> The last six return live discord.js objects on gateway and raw `discord-api-types` payloads on http. Every one of them carries `id` on both transports. The exception is an http `getMember` payload, which holds the guild fields alone, since Discord sends that person's user object once under `resolved.users`.
>
> `getUser` carries `username`, and `getMentionable` carries it when someone picked a user. A role has `name`, a channel has `name`, and an attachment has `filename`.

`getMember` returns `null` even for a required user option, since the person picked may have left the server.

Those are the option getters. On gateway the rest of the discord.js resolver is on `this.event.options`.

## Choices

Adding choices narrows the return to those values.

```ts title="src/commands/Probe.ts"
@RegisterCommand('global')
export class Probe extends BuilderComponent<'command'> {
    constructor() {
        super('command');

        this.instance
            .setName('probe')
            .setDescription('Search the catalogue')
            .addStringOption((option) =>
                option
                    .setName('category')
                    .setDescription('Scope the query')
                    .addChoices(
                        { name: 'Books', value: 'books' },
                        { name: 'Films', value: 'films' }
                    )
            );
    }
}
```

The getter gives back the union of the values, so a `switch` over it is exhaustive.

```ts
const category = this.options.getString('category');
```

## Channel types

`addChannelTypes` narrows `getChannel` to the types you listed. `target` above allows text and announcement channels, so `send` is there to call.

```ts
const target = this.options.getChannel('target');
```

Without `addChannelTypes` you get every channel kind Discord might send, so `send` is no longer guaranteed. A forum channel doesn't have it.

Threads are the one pair you cannot split. Asking for `PublicThread` alone gives you `PublicThread | AnnouncementThread`, since discord.js types a thread's `type` as both at once.

## When a getter is missing

A getter only exists when the command declares an option of that kind. `maintenance` declares no integer option, so `this.options.getInteger` does not exist there.

```ts
const count = this.options.getInteger('count');
```

Seeing this error on a getter you believe you declared means codegen hasn't run since. Run it to update the types.
