TypeScript интеграция и типизация

Lit полностью совместим с TypeScript, что позволяет создавать высоко типизированные веб-компоненты. TypeScript обеспечивает статическую проверку типов, автодополнение и поддержку современных возможностей ECMAScript, делая разработку компонентов более безопасной и предсказуемой.

Для начала работы с TypeScript достаточно установить необходимые зависимости и настроить компилятор:

npm install --save lit
npm install --save-dev typescript

Создаётся файл tsconfig.json, где важно указать следующие ключевые опции:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Node",
    "lib": ["DOM", "ES2022"],
    "strict": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "esModuleInterop": true
  }
}

Опции experimentalDecorators и emitDecoratorMetadata нужны для работы с декораторами Lit, такими как @property.


Типизация свойств компонентов

Lit предоставляет декоратор @property, который позволяет описывать свойства веб-компонента. TypeScript усиливает его возможностями строгой типизации:

import { LitElement, html, css } from 'lit';
import { property } from 'lit/decorators.js';

class MyComponent extends LitElement {
  @property({ type: String })
  name: string = 'Lit';

  @property({ type: Number })
  count: number = 0;

  @property({ type: Boolean })
  visible: boolean = true;

  render() {
    return html`
      <div ?hidden=${!this.visible}>
        ${this.name}: ${this.count}
      </div>
    `;
  }
}

customElements.define('my-component', MyComponent);

Ключевые моменты:

  • type в @property используется для конвертации значений атрибутов HTML в нужный тип.
  • TypeScript позволяет строго следить за типами свойств (string, number, boolean), предотвращая ошибки при присваивании.
  • При изменении свойства Lit автоматически обновляет шаблон, сохраняя реактивность компонента.

Типизация событий и кастомных событий

Lit поддерживает генерацию кастомных событий через метод dispatchEvent. С TypeScript можно описать типы события, чтобы упростить работу с слушателями:

class MyComponent extends LitElement {
  private _count: number = 0;

  increment() {
    this._count++;
    this.dispatchEvent(new CustomEvent<'increment' | 'change'>('change', {
      detail: { count: this._count }
    }));
  }
}

При использовании с TypeScript можно создавать интерфейсы для detail событий:

interface CountChangeDetail {
  count: number;
}

this.dispatchEvent(new CustomEvent<CountChangeDetail>('change', {
  detail: { count: this._count }
}));

Это позволяет при обработке события точно знать структуру event.detail:

element.addEventListener('change', (event) => {
  const detail = (event as CustomEvent<CountChangeDetail>).detail;
  console.log(detail.count);
});

Типизация слотов и шаблонов

Lit использует HTML-шаблоны через функцию html. Для строгой типизации можно определить интерфейсы для структуры данных, которая рендерится:

interface User {
  id: number;
  name: string;
}

class UserList extends LitElement {
  @property({ type: Array })
  users: User[] = [];

  render() {
    return html`
      <ul>
        ${this.users.map(user => html`<li>${user.name}</li>`)}
      </ul>
    `;
  }
}

TypeScript позволяет следить, чтобы users всегда был массивом объектов с нужными полями. Любая попытка передать другой тип вызовет ошибку на этапе компиляции.


Типизация реактивных свойств с геттерами и сеттерами

Lit поддерживает реактивные свойства через геттеры и сеттеры. TypeScript обеспечивает строгий контроль типов:

class CounterElement extends LitElement {
  private _count = 0;

  @property({ type: Number })
  get count(): number {
    return this._count;
  }

  set count(value: number) {
    const oldValue = this._count;
    this._count = value;
    this.requestUpdate('count', oldValue);
  }
}
  • Использование requestUpdate позволяет вручную инициировать обновление при изменении внутреннего состояния.
  • Типизация геттера и сеттера гарантирует правильный тип данных для свойства.

Использование обобщённых типов (Generics)

Для более сложных компонентов можно применять обобщения TypeScript, обеспечивая типовую гибкость:

class DataList<T> extends LitElement {
  @property({ type: Array })
  items: T[] = [];

  render() {
    return html`
      <ul>
        ${this.items.map(item => html`<li>${JSON.stringify(item)}</li>`)}
      </ul>
    `;
  }
}

interface Product {
  id: number;
  title: string;
}

const productList = new DataList<Product>();
productList.items = [{ id: 1, title: 'Laptop' }];

Обобщения позволяют создавать универсальные компоненты, которые строго типизированы, но при этом остаются гибкими для разных данных.


Поддержка строгой типизации в декораторах Lit

Декораторы @property, @state, @query и @queryAll полностью поддерживают TypeScript, что позволяет использовать автодополнение и проверку типов даже для внутренних состояний и элементов DOM:

import { state, query } from 'lit/decorators.js';

class InputComponent extends LitElement {
  @state()
  private value: string = '';

  @query('input')
  private inputElement!: HTMLInputElement;

  updateValue() {
    this.value = this.inputElement.value;
  }
}
  • @state() помечает внутреннее состояние, которое не отражается в атрибутах HTML.
  • @query() возвращает строго типизированный DOM-элемент.

Итоговые преимущества интеграции TypeScript с Lit

  • Полная проверка типов на этапе компиляции.
  • Автодополнение и документация прямо в IDE.
  • Более безопасная работа с событиями, свойствами и шаблонами.
  • Возможность создания универсальных и реактивных компонентов с строгой типизацией.

TypeScript в связке с Lit превращает веб-компоненты из простых блоков интерфейса в мощные, масштабируемые и безопасные элементы фронтенда.