#Assert fields are defined on an object generically

67 messages · Page 1 of 1 (latest)

peak blade
#

Hi, does someone know why .maxSlippage is still possibly undefined on the last lines?

type ConversationStep<T> = (
  conversation: Conversation<SessionCtx>,
  ctx: SessionCtx
) => Promise<any>;

/**
 * Class that represents a conversation
 */
export abstract class PerpieConversation<T extends {}> {
  /**
   * The arguments storage of this conversation.
   * It needs to be fully completed at the end of the conversation.
   */
  protected abstract arguments: Partial<T>;

  /**
   * The steps of this conversation.
   * A conversation is just a set of steps, each step may do some stuff
   * and set some #arguments values, that the next steps can use, until conversation is complete.
   *
   * Each step can accept keys of T, which asserts these fields are done before moving onto it
   * (compile time).
   */
  protected abstract steps: ConversationStep<T>[];

  // ======= Internal ======== //
  protected assertValues<K extends keyof T>(
    keys: K[]
  ): this is PerpieConversation<T> & {
    arguments: RequireFields<Partial<T>, K>;
  } {
    for (const key of keys) {
      if (!this.arguments[key]) {
        throw `Failure In ${
          this.#id
        } Conversation - Requested ${keys} to be defined, but ${String(
          key
        )} was not defined. Arguments state: ${this.arguments}`;
      }
    }

    return true;
  }
}

class OpenPosition extends PerpieConversation<IncreasePositionParams> {
  constructor(
    id: ACTION_NAMES,
    preparedArguments: Partial<IncreasePositionParams> = {}
  ) {
    super(id, preparedArguments);
  }

  protected arguments: Partial<IncreasePositionParams> = {};

  steps: ConversationStep<IncreasePositionParams>[] = [
    async () => {
      this.assertValues(["maxSlippage"]);
      this.arguments.maxSlippage + 5;
    },
  ];
}
#

basically, i just want this function to assert that the keys of the arguments i pass to it are defined on this.arguments

#

Also the typing on asset values seem to be correct

#

if i do if (!this.assetValues..) {return;} then it is correct

alpine dock
#

I believe because you're throwing in the type guard, but TS connects the type narrowing only to the boolean returned by the assertValues() fn. Try if (this.assertValues(['maxSlippage']))

halcyon ibex
#

do you want : asserts this is ... instead of : this is ...?

peak blade
#

the end goal here is to not even call this function inside each "step" but rather have the loop that is calling them assert it for them

peak blade
#

thank you so much

#

i know have another question, if you don't mind

#

i would like each of these steps to not only be a function, but rather an object with run() and prerequisites.

With prerequisites being an array of keys of T, similar to what assertValues currently accept

And then i would like to assert these prerequisties in the caller of that step, and input these asserted args into it

do you think that's possible? i've been trying a few things but everything ended up throwing

#

type Step<T,K extends keyof T> = {
prerequisites: ["maxSlippage", "collateral"]
run: (conversation: conversation, ctx: Ctx, args: RequireFields<Partial<T>, K>) => 
 }



class MyClass {

runSteps() {

for (const step of this.steps) {

assertDefinedValues(step.prerequiqesties)


await step(conv, ctx, this.args)
}
}
}

#

something like this @halcyon ibex

only that this specific implementaiton is not correct itthrows errors

#

that way i do not have to call assertValues at the top of every function, and there is a nice view of what is being assinged at each step

halcyon ibex
#

could you put together a playground with your existing code and a more complete example of what you want? i started one already but it's missing some stuff:

jolly solsticeBOT
#
mkantor#0

Preview:ts // just trying to stub stuff out here: type Conversation<T> = { Conversation: T } type SessionCtx = 'SessionCtx' type RequireFields<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>> type IncreasePositionParams = {} type ACTION_NAMES = 'ACTION_NAMES' ...

peak blade
#

yes one sec

peak blade
halcyon ibex
#

you gotta resend the link

peak blade
#

how do i save my changes?

halcyon ibex
#

the playground is stateless, everything is in the URL hash

peak blade
halcyon ibex
#

there's a link shortener plugin you can use. check plugins in the right sidebar

peak blade
#

of discord server or TS playground

halcyon ibex
#

TS playground

peak blade
halcyon ibex
#

it's not even that long lol

peak blade
halcyon ibex
#

okay so what part do you want to focus on? i'm not connecting this to your question about run()

#

also heads up i gotta run soon, but if we don't reach conclusion here maybe somebody else will jump in, or i might be able to check back some time tomorrow

halcyon ibex
#

you want to evolve the ConversationStep type in some way?

peak blade
#

right now, i would manually call assertValues[val,val1,val2 in each step

#

what i want to do is, have each step this kind of object:

type ConversationStep = {
prerequisites: ["maxSlippage", "collateral"] // etc, keys of T
run: (conv: Conversation, ctx: Ctx, args: RequireFields<T, allOfTheKeysInPrerequisites>)
}



run() {

const step = this.steps[i]
this.assertDefinedValues(step.prerequisties);

await step.run(conv, ctx,this.args)
}
peak blade
halcyon ibex
#

still a bit confused. would you have a different ConversationStep type for each step, each with different prerequisites? or is the code above not meant to be taken literally and instead ConversationStep would be be parameterized by its prerequisites?

peak blade
#

steps: [

{
prerequisites: [],
run: () => {
this.args.maxSlippage = 50;
}
},

{
prerequisites: ["maxSlippage"],
run: () => {
this.collateral = 50 * this.args.maxSlippage
}
},

{
prerequisites: ["maxSlippage", "collateral"]
run: () => {
side = this.collateral + this.args.maxSlippage / 50
}
}
// ...etc
]

#

@halcyon ibex like this

halcyon ibex
#

i see, and you want this to be magically auto-narrowed in run based on the elements of prerequisites

peak blade
#

since i assume as you said it shouldnt be possible to magically allow it to know

peak blade
#

if that makes sense @halcyon ibex

halcyon ibex
#

hmm, i'm not sure if it's possible because of the array of steps. i think you'd need variadic type parameters or something like that to be able to say "each element in the steps array follows a specific pattern (but that pattern is different from other elements in the array)"

#

i sketched this but as you can see it requires an explicit type annotation on args in run:

peak blade
#

do you think it's not possible then?

#

without inserting manual on args

halcyon ibex
#

i don't want to 100% rule it out yet but i'm thinking not

#

i have to run now though so can't give it more thought right now

peak blade
#

nw

#

thank you for your help man 🙏

halcyon ibex
#

feel free to open another help thread or something, maybe somebody else will have ideas

#

sure thing

#

good luck!

peak blade
#

thanks!

alpine dock
peak blade