---
title: "TypeScript Generics"
framework: javascript
version: "36.1.0"
---

# TypeScript Generics

AG Grid supports TypeScript [Generics](https://www.typescriptlang.org/docs/handbook/2/generics.html) for row data, cell values and grid context. This leads to greatly improved developer experience via code completion and compile time validation of row data and cell value properties.

## Row Data: <TData>

Provide a TypeScript interface for row data to the grid to enable auto-completion and type-checking whenever properties are accessed from a row `data` variable. There are multiple ways to configure the generic interface: via the `GridOptions<TData>` interface, via other individual interfaces and finally via framework components.

In the examples below we will use the `ICar` interface to represent row data.

```js
// Row Data interface
interface ICar {
    make: string;
    model: string;
    price: number;
}
```

### Configure via GridOptions

Set the row data type on the grid options interface via `GridOptions<ICar>`. The `ICar` interface will then be used throughout the grid options whenever row data is present. This is true for: properties, callbacks, events and the gridApi.

```js
// Pass ICar to GridOptions as a generic
const gridOptions: GridOptions<ICar> = {
    // rowData is typed as ICar[]
    rowData: [ { make: 'Ford', model: 'Galaxy', price: 20000 } ],

    // Callback with params type: GetRowIdParams<ICar>
    getRowId: (params) => {
        // params.data : ICar
        return params.data.make + params.data.model;
    },

    // Event with type: RowSelectedEvent<ICar>
    onRowSelected: (event) => {
        // event.data: ICar | undefined
        if (event.data) {
            const price = event.data.price;
        }
    }
}

// Grid Api methods use ICar interface
function onSelection() {
    // api.getSelectedRows() : ICar[]
    const cars: ICar[] = api!.getSelectedRows();
}
```

> **Note**
>
> You do not need to explicitly type callbacks and events that are defined as part of `GridOptions`. TypeScript will correctly pass the generic type down the interface hierarchy.

### Configure via Interfaces

Each interface that accepts a generic type of `TData` can also be configured individually. For example, an event handler function can accept the generic parameter on the event `RowSelectedEvent`.

```ts
function onRowSelected(event: RowSelectedEvent<ICar>) {
    if (event.data) {
        // event.data: ICar | undefined
        const price = event.data.price;
    }
}
```

### Type: TData | undefined

For a number of events and callbacks, when a generic interface is provided, the `data` property is typed as `TData | undefined` instead of `any`. The undefined is required because it is possible for the `data` property to be undefined under certain grid configurations.

A good example of this is [Row Grouping](https://www.ag-grid.com/javascript-data-grid/grouping/). The `onRowSelected` event is fired for both leaf and group rows. Data is only present on leaf nodes and so the event should be written to handle cases when `data` is undefined for groups.

```js
function onRowSelected(event: RowSelectedEvent<ICar>) {
    // event.data is typed as ICar | undefined
    if (event.data) {
        // Leaf row with data
        const price = event.data.price;
    } else {
        // This is a group row
    }
}
```

## Cell Value: <TValue>

When working with cell values it is possible to provide a generic interface for the `value` property. While this will often be a primitive type, such as `string` or `number`, it can also be a complex type. Using a generic for the cell value will enable auto-completion and type-checking.

### Configure via ColDef

Set the cell value type directly on the column definition interface via `ColDef<TData, TValue>` (e.g. `ColDef<ICar, number>`). This will be passed through to all properties in the column definition that use the cell value type.

### Configure via Interfaces

Each interface that accepts a generic type of `TValue` can also be configured individually. Here is an example of a `valueFormatter` for the price column. The `params.value` property is correctly typed as a `number` due to typing the params argument as `ValueFormatterParams<ICar, number>`.

```js
const colDefs: ColDef<ICar>[] = [
    {
        field: 'price',
        valueFormatter: (params: ValueFormatterParams<ICar, number>) => {
            // params.value : number
            return "£" + params.value;
        }
    }
];
```

The `TValue` generic type is also supported for cell renderers / editors by `ICellRendererParams<TData, TValue>` and `ICellEditorParams<TData, TValue>` respectively.

### Typed: TValue | null | undefined

For a number of events and callbacks when a generic interface is provided, the `value` property is typed as `TValue | null | undefined` instead of `any`. This is because it is possible for the `value` property to be `undefined` under certain grid configurations, and it can be `null` when cell editing is enabled and the value has been deleted.

## Context: <TContext>

The grid options property `context` can be used to provided additional information to grid callbacks and event handlers implemented by your application. See [Context](https://www.ag-grid.com/javascript-data-grid/context/) for more details. The `params.context` property can be typed via the `TContext` generic parameter.

### Configure via Interfaces

The generic parameter `TContext` needs to be explicitly provided to each interface where it is used. For example, an event handler function can accept the generic parameter on the event `RowSelectedEvent<TData, TContext>`.

```js
// Define the interface for your context
interface IDiscountRate {
    discount: number;
}

// Set the context property on gridOptions using `as` to apply the type
const gridOptions: GridOptions<ICar> {
    context: {
        discount: 0.9
    } as IDiscountRate;
}

// Provide to the interface to the TContext generic parameter to type the params.context property
function onRowSelected(event: RowSelectedEvent<ICar, IDiscountRate>) {
    if (event.data) {
        // event.context.discount is typed as number
        const price = event.data.price * event.context.discount;
    }
}
```

## Generic Type Example

Inspect the code in the following example or open in Plunker to experiment with generic typing yourself.

- `rowData` is typed using the `ICar` interface via `TData`.
- `valueFormatter` types the `value` property as `number` via `TValue`.
- `onRowSelected` event handler uses the `IDiscountRate` interface via `TContext`.

Also note that the `Log Selected Cars` button will log the selected cars to the developer console.

#### Generic Types

```ts
import {
  ClientSideRowModelModule,
  ColDef,
  GetRowIdParams,
  GridApi,
  GridOptions,
  ModuleRegistry,
  RowSelectedEvent,
  RowSelectionModule,
  ValueFormatterParams,
  createGrid,
  enableDevValidations,
} from "ag-grid-community";

// Enable extended validations only for development
if (process.env.NODE_ENV !== "production") {
  enableDevValidations();
}

ModuleRegistry.registerModules([RowSelectionModule, ClientSideRowModelModule]);

interface ICar {
  make: string;
  model: string;
  price: number;
}

interface IDiscountRate {
  discount: number;
}

const columnDefs: ColDef<ICar>[] = [
  { headerName: "Make", field: "make" },
  { headerName: "Model", field: "model" },
  {
    headerName: "Price",
    field: "price",
    valueFormatter: (params: ValueFormatterParams<ICar, number>) => {
      // params.value: number
      return "£" + params.value;
    },
  },
];

// Data with ICar interface
const rowData: ICar[] = [
  { make: "Toyota", model: "Celica", price: 35000 },
  { make: "Ford", model: "Mondeo", price: 32000 },
  { make: "Porsche", model: "Boxster", price: 72000 },
];

let gridApi: GridApi<ICar>;

// Pass ICar as generic row data type
const gridOptions: GridOptions<ICar> = {
  columnDefs,
  rowData,
  rowSelection: {
    mode: "multiRow",
  },
  context: {
    discount: 0.9,
  } as IDiscountRate,
  // Type specified her but can be omitted and inferred by Typescript
  getRowId: (params: GetRowIdParams<ICar>) => {
    // params.data : ICar
    return params.data.make + params.data.model;
  },
  onRowSelected: (event: RowSelectedEvent<ICar, IDiscountRate>) => {
    // event.data: ICar | undefined
    if (event.data && event.node.isSelected()) {
      const price = event.data.price;
      // event.context: IContext
      const discountRate = event.context.discount;
      console.log("Price with 10% discount:", price * discountRate);
    }
  },
};

function onShowSelection() {
  // api.getSelectedRows() : ICar[]
  const cars: ICar[] = gridApi!.getSelectedRows();
  console.log(
    "Selected cars are",
    cars.map((c) => `${c.make} ${c.model}`),
  );
}

// wait for the document to be loaded, otherwise
// AG Grid will not find the div in the document.
// lookup the container we want the Grid to use
const eGridDiv = document.querySelector<HTMLElement>("#myGrid")!;

// create the grid passing in the div to use together with the columns & data we want to use
gridApi = createGrid(eGridDiv, gridOptions);

if (typeof window !== "undefined") {
  // Attach external event handlers to window so they can be called from index.html
  (<any>window).onShowSelection = onShowSelection;
}
```

[Live example: Generic Types](https://www.ag-grid.com/examples/typescript-generics/generic/typescript)

## Fallback Default

If generic interfaces are not provided then the grid will use the default type of `any`. This means that generics in AG Grid are completely optional. GridOptions is defined as `GridOptions<TData = any>`, so if a generic parameter is not provided then `any` is used in its place for row data properties.

Likewise for cell values, if a generic parameter is not provided, `any` is used for the value property. For example, cell renderer params are defined as `ICellRendererParams<TData = any, TValue = any>`.
