> Markdown version of [Type Definitions](https://vaadin.com/docs/next/hilla/lit/components/type-definitions). Section index: [llms.txt](https://vaadin.com/docs/next/hilla/llms.txt)

# Vaadin Component Type Definitions for TypeScript

Vaadin components come with TypeScript definitions located in `d.ts` files published to `npm` together with the source code.

Using proper types for the web components helps to make the client-side logic more reliable. Depending on the IDE you use, TypeScript definitions can also enable better code completion and auto-import.

When using Visual Studio Code, it’s recommended that you install the [`lit-plugin`](https://marketplace.visualstudio.com/items?itemName=runem.lit-plugin), which provides syntax highlighting and type-checking support for Lit templates. You can also use the [`lit-analyzer`](https://www.npmjs.com/package/lit-analyzer) CLI tool by the same author for type checking.

## <a id="importing"></a>Importing Type Declarations

Every TypeScript definition file exports type declarations, which describe the public API provided by the web component. To use the type declarations in your client-side views, you need to import them using the [type-only import syntax](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-exports):

```typescript
import type { Dialog } from '@vaadin/dialog';
```

The type declarations are only used by the TypeScript compiler to analyze statically the code and collect information about types. They’re removed during the `Vite` compilation step and don’t end up in the resulting JavaScript bundle.

The following sections give examples of how to improve the developer experience when using Vaadin components by applying correct types for elements, their properties, and events.

## <a id="query-decorator"></a>The `@query` Decorator

Lit discourages manual DOM operations in favor of binding data to the component template declaratively using reactive properties. However, it can sometimes be convenient to get a reference to the component instance and use it for method calls.

The suggested way of storing a reference in Lit-based views is the `@query` decorator. This expects the type of the corresponding web component to be specified:

```typescript
@query('#dialog')
private dialog!: Dialog;
```

Note the `!` sign used in the property declaration. This is the [definite assignment assertion](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#definite-assignment-assertions) operator. It tells the TypeScript compiler that the `dialog` property is initialized indirectly and doesn’t need a default value.

## <a id="event-listeners"></a>Event Listeners

When using event listeners in Lit-based views, it might be useful to get a reference to the event target and then access its value. One common case for this is handling a `change` event.

The Lit syntax for binding listeners doesn’t let TypeScript know the type of component to which the listener belongs. You need to handle this manually by specifying the correct type:

```typescript
render() {
  return html`
    <vaadin-text-field @change="${this.onChange}"></vaadin-text-field>
  `;
}

onChange(event: Event) {
  const field = event.composedPath()[0] as TextField;
  // <vaadin-text-field> has a value property.
  console.log(field.value);
}
```

The `as` keyword is a [type assertion](https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions), often called a "type cast". It’s a hint for the TypeScript compiler, forcing it to use the type provided explicitly. In this particular case, it’s safe, as the component that dispatches the event is known in advance.

## <a id="custom-events"></a>Custom Events

Vaadin components offer type definitions for custom events, making it easier to use them in TypeScript. In particular, this allows you to get a proper type for the `detail` property, which has the type of `any` by default. To use the event types, you need to import them manually:

```typescript
import type { TextFieldInvalidChangedEvent } from '@vaadin/text-field';

render() {
  return html`
    <vaadin-text-field
      label="Username"
      @invalid-changed="${this.onInvalidChanged}"
    ></vaadin-text-field>
  `;
}

onInvalidChanged(event: TextFieldInvalidChangedEvent) {
  // `detail` is an object with boolean `value` property.
  console.log(event.detail.value);
}
```

## <a id="renderer-functions"></a>Renderer Functions

A renderer function is a class method used to render parts of the DOM of a component. Renderer functions accept one or more arguments. To access renderer functions without TypeScript warnings, you need to get them typed by importing and using the corresponding type declarations:

```typescript
protected indexRenderer(
  root: HTMLElement,
  column: GridColumn,
  model: GridItemModel<Person>
) {
  render(html`<div>${model.index}</div>`, root);
}
```

The `GridItemModel` type declaration is exported by the `@vaadin/grid` npm package. It’s a TypeScript interface describing the properties available on the `model`, including `index`. The `GridColumn` declaration is the type of the `<vaadin-grid-column>` component.

See also the [Grid content](https://vaadin.com/docs/components/grid#content) example, which demonstrates how to define `renderer` functions on the `Grid` columns to provide rich content based on the column.

## <a id="generic-types"></a>Generic Types

Certain Vaadin components — namely Grid, Combo Box, CRUD, and Virtual List — support setting the `items` property as an array of objects. Typically, when you use a component, you know the expected type of the object in advance and can provide it, explicitly.

In TypeScript, this could be achieved by using [generic types](https://www.typescriptlang.org/docs/handbook/generics.html#generic-types). For example, a Grid type can be specified when using a `@query` decorator:

```typescript
@query('#grid')
private grid!: Grid<Person>;
```

This type isn’t inferred by the component, internally. Therefore, the same generic type needs to be passed to the `model` argument of the `renderer` function:

```typescript
nameRenderer(
  root: HTMLElement,
  column: GridColumn,
  model: GridItemModel<Person>
) {
  // `model` is an object with an `item` property of type `Person`
  const person = model.item;
  render(html`<div>${person.firstName} ${person.lastName}</div>`, root);
}
```

A type argument can be also used in event listeners to detect changes of some properties:

```typescript
onSelectedItemChanged(event: ComboBoxSelectedItemChangedEvent<Person>) {
  // `detail` is an object of a `value` property of type `Person`
  console.log(event.detail.value);
}
```

Generic type arguments can be passed to various properties and TypeScript interfaces listed below.

### <a id="combo-box-generic-types"></a>Combo Box Generic Types

The following Combo Box properties support generic types:

- `dataProvider`

- `filteredItems`

- `items`

- `renderer`

- `selectedItem`

The following Combo Box interfaces support generic type arguments:

- `ComboBoxDataProvider`

- `ComboBoxDataProviderCallback`

- `ComboBoxItemModel`

- `ComboBoxRenderer`

- `ComboBoxSelectedItemChangedEvent`

### <a id="crud-generic-types"></a>CRUD Generic Types

The following CRUD properties support generic types:

- `dataProvider`

- `editedItem`

- `items`

The following CRUD interfaces support generic type arguments:

- `CrudCancelEvent`

- `CrudDataProviderCallback`

- `CrudDataProvider`

- `CrudDeleteEvent`

- `CrudEditEvent`

- `CrudEditedItemChangedEvent`

- `CrudItemsChangedEvent`

- `CrudSaveEvent`

### <a id="grid-generic-types"></a>Grid Generic Types

The following Grid properties support generic types:

- `activeItem`

- `cellClassNameGenerator`

- `dataProvider`

- `dragFilter`

- `dropFilter`

- `expandedItems`

- `items`

- `rowDetailsRenderer`

- `selectedItems`

The following Grid column properties support generic types:

- `footerRenderer`

- `headerRenderer`

- `renderer`

The following Grid interfaces support generic type arguments:

- `GridActiveItemChangedEvent`

- `GridBodyRenderer`

- `GridCellActivateEvent`

- `GridCellClassNameGenerator`

- `GridCellFocusEvent`

- `GridColumnReorderEvent`

- `GridColumnResizeEvent`

- `GridDataProvider`

- `GridDragAndDropFilter`

- `GridDragStartEvent`

- `GridDropEvent`

- `GridExpandedItemsChangedEvent`

- `GridEventContext`

- `GridItemModel`

- `GridRowDetailsRenderer`

- `GridSelectedItemsChangedEvent`

### <a id="virtual-list-generic-types"></a>Virtual List Generic Types

The following Virtual List properties support generic types:

- `items`

- `renderer`

The following Virtual List interfaces support generic type arguments:

- `VirtualListItemModel`

- `VirtualListRenderer`

## <a id="registering-elements"></a>Registering Elements

When creating custom elements to use with client-side views, you might want to instruct TypeScript to use your definitions. This isn’t required, but sometimes it improves the developer experience and allows you to write less code.

For example, if you use the `querySelector()` and `querySelectorAll()` methods in your custom element, which return an `Element` instance of an array of them, the easiest workaround would probably be to use a type cast:

```typescript
const items = this.renderRoot.querySelectorAll('color-item') as ColorItem[];
items.forEach(item => {
  // access item properties
});
```

However, this approach isn’t clean, as it requires you to write `as ColorItem[]` every time the method is called. A better alternative would be to register a class corresponding to the HTML tag name in the built-in `HTMLElementTagNameMap` interface:

```typescript
declare global {
  interface HTMLElementTagNameMap {
    'color-item': ColorItem;
  }
}
```

Now, every time you call `querySelector()` or `querySelectorAll()` with a corresponding tag name, the TypeScript compiler can automatically infer the proper type, making the type cast no longer necessary:

```typescript
const items = this.renderRoot.querySelectorAll('color-item');
items.forEach(item => {
  // access item properties
});
```

The TypeScript definitions for Vaadin components provide these registrations. This allows you to avoid writing type casts when using certain DOM methods. Apart from the query methods, this applies to other methods, such as `createElement()` and `closest()`.
