From 68f1e638aa2681fe804f3d64e17d99427dd10fa5 Mon Sep 17 00:00:00 2001 From: Tim Smart Date: Thu, 8 Dec 2022 11:58:05 +1300 Subject: [PATCH] refactor: better option type safety --- examples/package.json | 8 +++++- examples/pnpm-lock.yaml | 12 ++++++--- examples/src/interactions.ts | 42 +++++++++++++++++++++++++++++-- src/Interactions/definitions.ts | 44 ++++++++++++++++++++++----------- src/global.ts | 3 +-- src/index.ts | 3 +++ 6 files changed, 89 insertions(+), 23 deletions(-) diff --git a/examples/package.json b/examples/package.json index c9d3b33..767330c 100644 --- a/examples/package.json +++ b/examples/package.json @@ -12,7 +12,7 @@ "dependencies": { "@effect/io": "^0.0.30", "@fp-ts/data": "^0.0.20", - "dfx": "^0.14.0", + "dfx": "^0.15.0", "dotenv": "^16.0.3", "fastify": "^4.10.2" }, @@ -20,5 +20,11 @@ "esbuild": "^0.15.18", "ts-node": "^10.9.1", "typescript": "^4.9.3" + }, + "pnpm": { + "overrides": { + "@effect/io": "^0.0.30", + "@fp-ts/data": "^0.0.20" + } } } diff --git a/examples/pnpm-lock.yaml b/examples/pnpm-lock.yaml index d0b6346..1126dd1 100644 --- a/examples/pnpm-lock.yaml +++ b/examples/pnpm-lock.yaml @@ -1,9 +1,13 @@ lockfileVersion: 5.4 +overrides: + '@effect/io': ^0.0.30 + '@fp-ts/data': ^0.0.20 + specifiers: '@effect/io': ^0.0.30 '@fp-ts/data': ^0.0.20 - dfx: ^0.14.0 + dfx: ^0.15.0 dotenv: ^16.0.3 esbuild: ^0.15.18 fastify: ^4.10.2 @@ -13,7 +17,7 @@ specifiers: dependencies: '@effect/io': 0.0.30 '@fp-ts/data': 0.0.20 - dfx: 0.14.0_bonjnmg6oatqyr73vbdnw4brbi + dfx: 0.15.2_bonjnmg6oatqyr73vbdnw4brbi dotenv: 16.0.3 fastify: 4.10.2 @@ -331,8 +335,8 @@ packages: ms: 2.1.2 dev: false - /dfx/0.14.0_bonjnmg6oatqyr73vbdnw4brbi: - resolution: {integrity: sha512-2wFLurofJHU2dWSpm3OhMjUnC/hcl12sNMiCyEhnulO7krC/amC+BNGwN3KqMNzcXb/kiksPmZhfuCD0pAuCHQ==} + /dfx/0.15.2_bonjnmg6oatqyr73vbdnw4brbi: + resolution: {integrity: sha512-05tc6v8J52G+kTDKKquxyKquT18Zy7kdQsQWf2ofr9uhsOxOy19/Q6RuXFgYb9FZ85wrkmVMES48/vj6yjMiUQ==} peerDependencies: '@effect/io': '*' '@fp-ts/data': '*' diff --git a/examples/src/interactions.ts b/examples/src/interactions.ts index 0dc0616..a588848 100644 --- a/examples/src/interactions.ts +++ b/examples/src/interactions.ts @@ -2,7 +2,7 @@ import * as Cause from "@effect/io/Cause" import * as Effect from "@effect/io/Effect" import * as Exit from "@effect/io/Exit" import { pipe } from "@fp-ts/data/Function" -import { Ix } from "dfx" +import { Discord, Ix } from "dfx" import { make, runIx } from "dfx/gateway" import Dotenv from "dotenv" @@ -28,8 +28,46 @@ const hello = Ix.global( }), ) +// Optionally use the type safe helpers +const greeting = Ix.global( + { + name: "greeting", + description: "A basic command", + options: [ + { + type: Discord.ApplicationCommandOptionType.STRING, + name: "who", + description: "who to greet", + required: true, + }, + { + type: Discord.ApplicationCommandOptionType.STRING, + name: "greeting", + description: "What kind of greeting?", + }, + ], + }, + (i) => + pipe( + Effect.struct({ + who: i.optionValue("who"), + greeting: pipe( + i.optionValueOptional("greeting"), + Effect.someOrElse(() => "Hello"), + ), + // fail: i.optionValue("fail"), // <- this would be a type error + }), + Effect.map(({ who, greeting }) => ({ + type: 4, + data: { + content: `${greeting} ${who}!`, + }, + })), + ), +) + // Build your program use `Ix.builder` -const ix = Ix.builder.add(hello) +const ix = Ix.builder.add(hello).add(greeting) // Run it pipe( diff --git a/src/Interactions/definitions.ts b/src/Interactions/definitions.ts index 2d60f84..76ad2ec 100644 --- a/src/Interactions/definitions.ts +++ b/src/Interactions/definitions.ts @@ -150,7 +150,7 @@ export interface CommandHelper { > optionValue: ( - name: CommandOptions["name"], + name: RequiredCommandOptions["name"], ) => Effect optionValueOptional: ( @@ -166,7 +166,7 @@ export interface CommandHelper { > subCommandOptionValue: ( - name: SubCommandOptions["name"], + name: RequiredSubCommandOptions["name"], ) => Effect subCommandOptionValueOptional: ( @@ -174,10 +174,12 @@ export interface CommandHelper { ) => Effect> subCommands: < - NER extends Record< - SubCommands["name"], - Effect - >, + NER extends SubCommands extends never + ? never + : Record< + SubCommands["name"], + Effect + >, >( commands: NER, ) => Effect< @@ -207,23 +209,23 @@ type CommandHandlerFn = ( // Extract option names type ExtractOptions = A extends { - name: string type: T + name: string options?: Discord.ApplicationCommandOption[] } - ? - | A - | (A extends { - options: Discord.ApplicationCommandOption[] - } - ? ExtractOptions - : never) + ? A : A extends { options: Discord.ApplicationCommandOption[] } ? ExtractOptions : never +type RequiredOptions = A extends { + options: Discord.ApplicationCommandOption[] +} + ? Extract + : never + type CommandOptions = ExtractOptions< A, Exclude< @@ -233,6 +235,15 @@ type CommandOptions = ExtractOptions< > > +type RequiredCommandOptions = RequiredOptions< + A, + Exclude< + Discord.ApplicationCommandOptionType, + | Discord.ApplicationCommandOptionType.SUB_COMMAND + | Discord.ApplicationCommandOptionType.SUB_COMMAND_GROUP + > +> + type SubCommands = ExtractOptions< A, Discord.ApplicationCommandOptionType.SUB_COMMAND @@ -243,6 +254,11 @@ type SubCommandOptions = Exclude< undefined >[number] +type RequiredSubCommandOptions = Extract< + Exclude["options"], undefined>[number], + { required: true } +> + type Resolvables = ExtractOptions< A, | Discord.ApplicationCommandOptionType.ROLE diff --git a/src/global.ts b/src/global.ts index e332217..b0f02b6 100644 --- a/src/global.ts +++ b/src/global.ts @@ -45,8 +45,7 @@ import type { Equal } from "@fp-ts/data/Equal" /** * @tsplus global */ -import type { Option as Maybe } from "@fp-ts/data/Option" -export type { Option as Maybe } from "@fp-ts/data/Option" +import type { Maybe } from "dfx" /** * @tsplus global diff --git a/src/index.ts b/src/index.ts index 1476f9c..8ecddab 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,3 +13,6 @@ export * as IxHelpers from "./Helpers/interactions.js" export * as Members from "./Helpers/members.js" export * as Perms from "./Helpers/permissions.js" export * as UI from "./Helpers/ui.js" + +// Hack for tsplus globals +export type { Option as Maybe } from "@fp-ts/data/Option"