#Restricting type values in a union

39 messages · Page 1 of 1 (latest)

harsh egret
#

I am having trouble wording this, however I have this:

//type ApplicationCommandData = UserApplicationCommandData | MessageApplicationCommandData | ChatInputApplicationCommandData
// (from discord.js)

type Foo = ApplicationCommandData | Bar;

interface Bar extends ChatInputApplicationCommandData {
  options?: readonly Exclude<
    ApplicationCommandOptionData,
    {
      type:
        | ApplicationCommandOptionType.SubcommandGroup
        | ApplicationCommandOptionType.Subcommand;
    }
  >[];
  handler(interaction: ChatInputCommandInteraction): Promise<void> | void;
}

function defineFoo(foo: Foo) {
  return foo;
}

defineFoo({
  name: "test",
  description: "test",
  options: [
    {
      type: ApplicationCommandOptionType.SubcommandGroup,
      name: "test",
      description: "test",
      options: [],
    },
  ],
  handler(interaction) {},
});

Why is options allowed to have SubcommandGroup, when Bar is the only type with handler() and it does not allow SubcommandGroup?

#

The options for ChatInputApplicationCommandData is readonly ApplicationCommandOptionData[], but shouldn't it be the excluded version because it has the handler that matches Bar?

#

It works fine when you change defineFoo(foo: Foo) to defineFoo(foo: Bar) but I want the option to do the original ChatInputApplicationCommandData and my Bar

pallid cedar
#

It's a lot easier if you make a TS Playground

sterile gyroBOT
#
sandiford#0

Preview:```ts
interface ApplicationCommandData {
a: string
b: string
}

type Foo = ApplicationCommandData | Bar

interface Bar extends ChatInputApplicationCommandData {
options?: readonly Exclude<
ApplicationCommandOptionData,
{
type:
| ApplicationCommandOptionType.SubcommandGroup
...```

pallid cedar
#

You can fill in the gaps and then copy the address and paste back here

rotund epoch
#

i already did the work 😅:

sterile gyroBOT
#
mkantor#0

Preview:```ts
import {
ApplicationCommandData,
ChatInputApplicationCommandData,
ApplicationCommandOptionData,
ApplicationCommandOptionType,
ChatInputCommandInteraction,
} from "discord.js"

type Foo = ApplicationCommandData | Bar

interface Bar extends ChatInputApplicationCommandData {
op
...```

pallid cedar
#

lol

#

Oh it's all Discord

harsh egret
#

😂 Wasnt sure how to do the imports haha

pallid cedar
#

Is it a type subtraction issue?

#

I guess mkantor will solve it 🙂

rotund epoch
#

Why is options allowed to have SubcommandGroup, when Bar is the only type with handler() and it does not allow SubcommandGroup?
the technical answer to this is that excess properties are generally fine. the warning you get for them is very conservative and only kicks in when TS is 100% certain the property can never be accessed

#

here's a much simpler/more-self-contained example:

sterile gyroBOT
#
type A = { a: true }
type B = { b: true }

function gimmeAOrB(x: A | B) {}

// no error:
gimmeAOrB({
  a: true,
  b: true,
})
harsh egret
#

That is cursed. 😭

rotund epoch
#

why? there's no problem with having a value that is assignable to both A and B

#

or in your case, ApplicationCommandData and Bar

#

is there a specific problem this is causing you? like does the presence of handler break something elsewhere?

harsh egret
#

If a discord command has a sub group or subcommand, the root command is not possible to be called.

#

Just preventing to define a handler that will never be called.

pallid cedar
#

You can set handler?: never on the union members that can't have a handler

rotund epoch
#

yeah, but i generally would not bother. values in TS can always have excess properties. it's just part of the nature of the type system. IMO it's better to embrace/expect it

harsh egret
pallid cedar
#

You can use undefined if you want to

harsh egret
#

ah

rotund epoch
harsh egret
#

Funnily enough I was ALMOST there

#

Just didnt specify that handler should not be defined, I just left it out 😂

#

But that works! Thanks!

#

!resolved

pallid cedar
#

I actually started working on implementing closed/exact types in TypeScript

#

I don't know if the maintainers would actually add it if completed though.

harsh egret
#

Wizard! 🪄

pallid cedar
#

tsc is surprisingly simply to work on really

#

It took me 2-3 days to do that, with no prior experience.