'How to properly fix 'no-implicit-any' when accessing unknown object member?

In a large project, I have a standard reporting module that displays in a grid data, and where the user can define filters and sorting.

Because I don't want to replicate logic, I setup a generic filtering function that can accept data from any type and filter options.

Everything is working perfectly, but ESlint is yelling because of the any usage.

Here's a simplified reproductible code (actual code contains a lot more rules and complexity, like string normalization and data sorting):

// In module utility

type reportState = {
  filterOn : string,
  filterValue : string
}

const filterDataPredicate = (row : any, viewOptions : reportState)=>{

  const dataKey = viewOptions.filterOn as keyof any;

  const valueToCheck = row[dataKey];

  return valueToCheck?.toString() === viewOptions.filterValue;

}

// In consuming module 1
type Point = {
  x: number,
  y: number
};

const graphData: Point[] = [
  { x: 0, y: 20 }, { x: -8, y: 12 }, { x: 100, y: 0 }
]

const currentGraphReportOptions = {
  filterOn : "x",
  filterValue : "0"
};
const filteredGraphData = graphData.filter((row)=>filterDataPredicate(row, currentGraphReportOptions));


console.log(filteredGraphData);

// In consuming module 2
type Customer = {
  firstName: string,
  lastName: string
};

const customerData: Customer[] = [
  { firstName: "John", lastName : "Smith"}, { firstName : "Jane", lastName:"Smith"},{ firstName : "Joe", lastName:"Dalton"}
]

const currentCustomerReportOptions = {
  filterOn : "lastName",
  filterValue : "Smith"
};
const filteredCustomerData = customerData.filter((row)=>filterDataPredicate(row, currentCustomerReportOptions));


console.log(filteredCustomerData);

Everything works as expected, except for the ESLint warning :

warning Unexpected any. Specify a different type @typescript-eslint/no-explicit-any

My IDE (VSCode) suggests me to either replace type by unknown or never, but none works.

When using unknown, row[dataKey] is not compiling (Object is of type 'unknown'.(2571)), while when using never, it tells me .toString() is not available on never type (Property 'toString' does not exist on type 'never'.(2339)).

Is there a clean way to fix that?

PS: if it matters, I enable strict : true in my tsconfig.json file, and in my eslint config I have :

{
  "rules": {
    "@typescript-eslint/no-explicit-any": "error"
  }
}


Solution 1:[1]

I managed to fix the issue (w/o disabling the rule)

The correct type for row object should Record<string, unknown>.

I think this is working because my incoming row will be non empty objects and nothing else (no base type, ...). Whatever the object values will be, it's always identified by an object key.

Updated code :

// In module utility


type reportState = {
  filterOn : string,
  filterValue : string
}

const filterDataPredicate = (row : Record<string, unknown>, viewOptions : reportState)=>{

  const dataKey = viewOptions.filterOn;

  const valueToCheck = row[dataKey];

  return `${valueToCheck}` === viewOptions.filterValue;

}

// In consuming module 1
type Point = {
  x: number,
  y: number
};

const graphData: Point[] = [
  { x: 0, y: 20 }, { x: -8, y: 12 }, { x: 100, y: 0 }
]

const currentGraphReportOptions = {
  filterOn : "x",
  filterValue : "0"
};
const filteredGraphData = graphData.filter((row)=>filterDataPredicate(row, currentGraphReportOptions));


console.log(filteredGraphData);

// In consuming module 2
type Customer = {
  firstName: string,
  lastName: string
};

const customerData: Customer[] = [
  { firstName: "John", lastName : "Smith"}, { firstName : "Jane", lastName:"Smith"},{ firstName : "Joe", lastName:"Dalton"}
]

const currentCustomerReportOptions = {
  filterOn : "lastName",
  filterValue : "Smith"
};
const filteredCustomerData = customerData.filter((row)=>filterDataPredicate(row, currentCustomerReportOptions));


console.log(filteredCustomerData);

Solution 2:[2]

You can change that eslint rule to "@typescript-eslint/no-explicit-any": "off". Then it won't complain about it anymore. You can also change it to warning if you want it to warn you but not throw an error.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Steve B
Solution 2 zlowe