---
product: "AG Grid"
title: "TypeScript Generics"
description: "AG Grid supports TypeScript 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."
framework: angular
version: "36.2.0"
llms: "https://www.ag-grid.com/archive/36.2.0/llms.txt"
---

# 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;
    }
}
```

### Configure via Component

The `<ag-grid-angular>` component is generic with respect to `TData`. As there is no way to explicitly set generic parameters on Angular components, Angular infers the `TData` generic type from Inputs / Outputs. However, it will not always be successful, leaving `TData` inferred as `any`.

We recommend providing the generic `TData` type to your `columnDefs` property as in practice we see this leading to the most accurate inference.

To provide the generic type of `ICar` to the `columnDefs` property you use the type `ColDef<ICar>` as follows:

```ts
const columnDefs: ColDef<ICar>[] = [...];
```

Then set this on your component template as `[columnDefs]="columnDefs"`. This provides Angular with the information it needs to assigns `ICar` to `TData` for the component.

```html
<ag-grid-angular
    [columnDefs]="columnDefs"
    (rowSelected)="onRowSelected($event)"
    />
```

This generic parameter is used for all other Inputs and Outputs ensuring consistency across the component. If `(rowSelected)` is defined with a different interface the application code will fail to compile.

```ts
// ERROR: INotACar is not assignable to ICar
onRowSelected(event: RowSelectedEvent<INotACar>) {}

// SUCCESS: ICar is correct interface
onRowSelected(event: RowSelectedEvent<ICar>) {}
```

### 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/archive/36.2.0/angular-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`. `value` is nullable because the grid calls these callbacks in situations where there is no underlying value to provide:

- Group rows, which have no row `data` of their own, so any `field`-based value is unresolved.
- Rows whose data has not yet loaded, e.g. with the [Server-Side Row Model](https://www.ag-grid.com/archive/36.2.0/angular-data-grid/server-side-model/).
- A `field` that is absent from the row data.

Callbacks must handle `null` and `undefined` themselves - the grid does not filter them out before invoking your callback.

Annotating `ColDef<TData, TValue>` (or the relevant params interface) turns property access and arithmetic on `params.value` into compile errors, e.g. `params.value.toFixed(2)` or `params.value * 2`. It does not catch every mistake: string concatenation and template interpolation, e.g. `'£' + params.value` or `` `${params.value}` ``, still compile even though they render `"undefined"` for an unhandled value, so these still need an explicit `== null` check.

## 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/archive/36.2.0/angular-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 { Component } from "@angular/core";
import { AgGridAngular } from "ag-grid-angular";
import "./styles.css";
import {
  ClientSideRowModelModule,
  ColDef,
  ColGroupDef,
  GetRowIdFunc,
  GetRowIdParams,
  GridApi,
  GridOptions,
  GridReadyEvent,
  ModuleRegistry,
  RowSelectedEvent,
  RowSelectionModule,
  RowSelectionOptions,
  ValueFormatterParams,
  enableDevValidations,
} from "ag-grid-community";

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

ModuleRegistry.registerModules([RowSelectionModule, ClientSideRowModelModule]);

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

@Component({
  selector: "my-app",
  standalone: true,
  imports: [AgGridAngular],
  template: `<div class="test-container">
    <div class="test-header">
      <button (click)="onShowSelection()">Log Selected Cars</button>
    </div>
    <ag-grid-angular
      style="width: 100%; height: 100%;"
      class="test-grid"
      [columnDefs]="columnDefs"
      [rowData]="rowData"
      [rowSelection]="rowSelection"
      [context]="context"
      [getRowId]="getRowId"
      (rowSelected)="onRowSelected($event)"
      (gridReady)="onGridReady($event)"
    />
  </div> `,
})
export class AppComponent {
  private gridApi!: GridApi<ICar>;

  columnDefs: ColDef[] = [
    { headerName: "Make", field: "make" },
    { headerName: "Model", field: "model" },
    {
      headerName: "Price",
      field: "price",
      valueFormatter: (params: ValueFormatterParams<ICar, number>) => {
        // params.value: number
        return "£" + params.value;
      },
    },
  ];
  rowData: ICar[] | null = [
    { make: "Toyota", model: "Celica", price: 35000 },
    { make: "Ford", model: "Mondeo", price: 32000 },
    { make: "Porsche", model: "Boxster", price: 72000 },
  ];
  rowSelection: RowSelectionOptions | "single" | "multiple" = {
    mode: "multiRow",
  };
  context: any = {
    discount: 0.9,
  } as IDiscountRate;
  getRowId: GetRowIdFunc = (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);
    }
  }

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

  onGridReady(params: GridReadyEvent<ICar>) {
    this.gridApi = params.api;
  }
}
```

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

## 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>`.
