When working on a project, it's very common to send complex data types from Lightning Web Components to Apex methods.

When it comes to sending primitive data types from LWC to Apex it's pretty much straight forward.

Assuming the apex method receives a param with the name searchText this is how the LWC code is going to look like.

In LWC we are sending data as an Object {searchText: 'United'} and It's a mandate that the key in this object is matching the Apex method param name.
import { api, LightningElement, wire } from "lwc";

import foobar from "@salesforce/apex/ExploreAccountsController.fetchAccounts";

export default class ExploreWiredApex extends LightningElement {
  @wire(foobar, { searchText: "United" })
  account({ error, data }) {
    if (data) {
      console.log(data);
    } else if (error) {
      console.log(error);
    }
  }
}
exploreWiredApex.js

Here is the Apex class!

public with sharing class ExploreAccountsController {
  public ExploreAccountsController() {
  }

  @AuraEnabled(cacheable=true)
  public static List<Account> fetchAccounts(String searchText) {
    String wildCharSearchText = '%' + searchText + '%';

    return [
      SELECT Id, Name, Industry, Rating
      FROM Account
      WHERE Name LIKE :wildCharSearchText
    ];
  }
}
ExploreAccountsController.cls

Now the catch is what if we are interested in sending complex data types from LWC to Apex methods.

If we send an Array in LWC then it will be received as a List in Apex method. Likewise, if we send an object in LWC then it will be received as a Map in the Apex method.

Here is some sample code!

import { api, LightningElement, wire } from "lwc";

import foobar from "@salesforce/apex/ExploreAccountsController.fetchAccounts";

const accNamesMap = {
  name: "Teja",
  profession: "Salesforce Developer",
  location: "World"
};

const accountsArray = ["united group", "sforce", "salesforce casts"];

export default class ExploreWiredApex extends LightningElement {
  @wire(foobar, {
    searchText: "United",
    accArray: accountsArray,
    accNamesIndustriesMap: accNamesMap
  })
  account({ error, data }) {
    if (data) {
      console.log(data);
    } else if (error) {
      console.log(error);
    }
  }
}
exploreWiredApex.js

Some changes have to be made to Apex class and the refactored Apex class is going to look like this.

public with sharing class ExploreAccountsController {
  public ExploreAccountsController() {
  }

  @AuraEnabled(cacheable=true)
  public static List<Account> fetchAccounts(
    String searchText,
    List<String> accArray,
    Map<Object, String> accNamesIndustriesMap
  ) {
    System.debug(' ๐Ÿš€ ' + accArray);
    System.debug(' ๐Ÿš€ ' + accNamesIndustriesMap);
    String wildCharSearchText = '%' + searchText + '%';

    return [
      SELECT Id, Name, Industry, Rating
      FROM Account
      WHERE Name LIKE :wildCharSearchText
    ];
  }
}
AccountsController.cls

Focus on the Array and the Object that we are sending in the LWC. In Apex class they are being received as a List and Map.

The key in an Object doesnโ€™t have quotes and hence in Apex method the Map has this signature <Object, String> (observe how I have used Object for the data type of keys)

Hope this is helpful!