1

I have a function that accepts a parameter which is an object consisting of a field (string) and arguments (object literal).

I'd like the arguments type check to be validated dependent on what the field is. I also wish to make a definition that can be extended wherever I needed (hence the generic).

Definition:

 export interface EventDefinition<
    TField extends string,
    TArgs extends any
  > {
    field: TField
    arguments: TArgs
  }

My function:


export const myFn =  (
  params:
    | EventDefinition<
        'actionOne',
        {
          id: string
        }
      >
    | EventDefinition<
        'actionTwo',
        {
          emailAddress: string
        }
      >
) => {
  const { args, field } = params

  switch(field) {
    case 'actionOne':
      console.log(args.id)
      break;
      case 'actionTwo':
      console.log(args.emailAddress)
      break;
  }
}

While the field property validates the args do not and result in errors (for ex with args.id):

Property 'id' does not exist on type '{ id: string }'.
Property 'id' does not exist on type '{ emailAddress: string; }'.

How can I correctly define this?

cyberwombat
  • 38,105
  • 35
  • 175
  • 251

1 Answers1

3

Typescript will not guard one variable based on another, this is just not supported.

If you use the argument itself control flow analysis can determine the types appropriately

  switch(params.field) {
    case 'actionOne':
      console.log(params.arguments.id)
      break;
      case 'actionTwo':
      console.log(params.arguments.emailAddress)
      break;
  }

Playground Link

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • Shoot. I saw an answer by you regarding Mapping Interface which I think should work albeit losing some flex. https://stackoverflow.com/questions/56949513/typescript-type-of-a-property-dependent-on-another-property-within-the-same-obj – cyberwombat Oct 26 '20 at 20:57
  • @cyberwombat you already have a discriminated union, just as I suggested in the other answer. Your only issue is that you can't type guard on separate values, you need to keep everything in one variable – Titian Cernicova-Dragomir Oct 26 '20 at 21:35