Angular 15 Event Binding Type Object Does not Accept Specific Function Type Declaration

In an angular 15 project, I have a component called in the html file by

<app-component
      (onAction)="action($event)">
</app-component>

and the invoked action function declared in the ts file as

action(event: { first_data_item: number; second_data_item: number; third_data_item: any; fourth_data_item: string }) {}

If enabling strict mode, I get error

Argument of type 'Object' is not assignable to parameter of type '{ first_data_item: number; second_data_item: number; third_data_item: any; fourth_data_item: string }'.

Without strict mode enabled, this works like a charm.

How should I specify the data sent with this function call?

>Solution :

The type of your function argument definition and your $event parameter must match.

We can’t see how you have defined your @Output() onAction in your app.component.ts but my guess is it is something generic like:

@Output() onAction: {};

Therefore TypeScript complains that onAction may be emitting an object with any keys/values and your consuming action() function is expecting specific keys/values, specifically:

{
  first_data_item: number;
  second_data_item: number;
  third_data_item: any;
  fourth_data_item: string
}

To resolve this specific clash in isolation you have two options:

  • Change your onAction type to match your action type, i.e. make onAction more specific
  • Change your action() type to match your onAction type, i.e. make action() less specific

However, I assume from the term action you are trying to build a state management solution and have therefore intentionally loosely typed onAction and specifically typed action as you are expecting to have multiple actions with different payloads (objects). Therefore neither of the above solutions will work for you.

A better way of doing this would be to leverage discriminated union types

actions.ts

type ActionOne = {
  type: 'actionOne',
  payload: {
    first_data_item: number;
    second_data_item: number;
    third_data_item: any;
    fourth_data_item: string
  }
}
type ActionTwo = {
  type: 'actionTwo',
  payload: {
    someOtherKey: any
  }
}

type Actions = ActionOne | ActionTwo

app.component.ts

@Output() onAction: Actions;

other.component.html

<app-component
  (onAction)="action($event)">
</app-component>

other.component.ts

action(event: Actions) {
  if (event.type === 'actionOne') {
    // It knows the type of payload is ActionOne['payload']
    event.payload.first_data_item // valid
    event.payload.someOtherKey // error
  }
  else {
    // It knows the type of payload is ActionTwo['payload']
    event.payload.first_data_item // error
    event.payload.someOtherKey // valid
  }
}

Leave a Reply