Search code examples
reactjstypescripttypescript-generics

How can I achieve inferred type on callback value based on property of the same object?


Im building a DataGrid component. I want to achieve a behavior were based on the row object type, I can infer the fields that can be declared as columns.

So the use case of the component would be:

const users: User[] = [
  {name: 'John', isActive: true, id: 1},
  {name: 'Mike', isActive: false, id: 2}
]


return <DataGrid rows={users} columns={[
  {field: 'name', formatter: (value) => value.toUpperCase()}, // value should be string
  {field: 'id', formatter: (value) => value}, // value should be number
  {field: 'isActive', formatter: (value) => value} // value should be boolean
]}/>

And Im typing the DataGrid props as

interface ColumnDefinition<T extends object, K extends keyof T> {
  field: K
  formatter(value: T[K]) => string
}

interface DataGridProps<T, K> {
  rows: T[]
  columns: ColumnDefinition<T, K>[]
}

The current behavior is that the field attribute is correctly inferring all the keys from user, but the value on the formatter callback is inferring the union of all the posibles values on user, like string | number | boolean

What am I missing here?


Solution

  • you can solve your problem by making your type like this :

    type ColumnDefinition<T extends object> = {
      [K in keyof T]: {
        field: K;
        formatter: (value: T[K]) => string;
      };
    }[keyof T];
    
    interface DataGridProps<T extends object> {
      rows: T[];
      columns: ColumnDefinition<T>[];
    }
    

    With this solution, ColumnDefinition becomes a union and typescript discriminates this using the value of the field attribute.

    I got my inspiration from this: https://github.com/microsoft/TypeScript/issues/36444#issuecomment-578572999