TypeScript типизация

Библиотека Shepherd.js предоставляет встроенные типы, что позволяет эффективно использовать её в проектах на TypeScript без необходимости писать собственные декларации. Корректная типизация облегчает разработку, повышает надежность кода и снижает количество ошибок при работе с турами.


Подключение и базовые типы

После установки пакета типы становятся доступны автоматически:

npm install shepherd.js

Импорт в TypeScript:

import Shepherd from 'shepherd.js';

Основные типы:

  • Shepherd.Tour — объект тура
  • Shepherd.Step — отдельный шаг
  • Shepherd.StepOptions — конфигурация шага
  • Shepherd.TourOptions — настройки тура

Типизация тура

Создание тура с явной типизацией:

const tour: Shepherd.Tour = new Shepherd.Tour({
  defaultStepOptions: {
    cancelIcon: {
      enabled: true
    }
  }
});

Тип TourOptions описывает структуру конфигурации:

const options: Shepherd.TourOptions = {
  useModalOverlay: true,
  exitOnEsc: true
};

Использование типа отдельно позволяет валидировать конфигурацию до создания экземпляра.


Типизация шагов

Шаги создаются с использованием StepOptions:

const stepOptions: Shepherd.StepOptions = {
  title: 'Заголовок',
  text: 'Описание шага',
  attachTo: {
    element: '.selector',
    on: 'bottom'
  }
};

Добавление шага:

tour.addStep(stepOptions);

Или напрямую:

tour.addStep({
  id: 'step-1',
  text: 'Первый шаг'
});

TypeScript проверяет:

  • наличие обязательных полей
  • корректность значений (on, buttons, when)
  • типы вложенных объектов

Строгая типизация attachTo

Поле attachTo имеет строгую структуру:

attachTo?: {
  element: string | HTMLElement | (() => HTMLElement);
  on: 
    | 'top'
    | 'bottom'
    | 'left'
    | 'right'
    | 'auto'
    | 'auto-start'
    | 'auto-end';
};

Пример с функцией:

attachTo: {
  element: () => document.querySelector('.dynamic') as HTMLElement,
  on: 'right'
}

Использование функции полезно, когда элемент появляется динамически.


Типизация кнопок

Кнопки шага имеют отдельный тип:

buttons: Array<{
  text: string;
  action: (this: Shepherd.Tour) => void;
  classes?: string;
}>;

Пример:

buttons: [
  {
    text: 'Далее',
    action() {
      this.next();
    }
  },
  {
    text: 'Назад',
    action() {
      this.back();
    }
  }
]

Особенность: this внутри action строго типизирован как Shepherd.Tour.


Типизация событий (when)

События задаются через объект when:

when: {
  show?: () => void;
  hide?: () => void;
  cancel?: () => void;
}

Пример:

when: {
  show() {
    console.log('Шаг показан');
  }
}

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


Расширение типов (кастомные поля)

Иногда требуется добавить собственные поля к шагам. Это достигается через расширение интерфейсов.

interface CustomStepOptions extends Shepherd.StepOptions {
  analyticsId?: string;
}

Использование:

const step: CustomStepOptions = {
  text: 'Шаг с аналитикой',
  analyticsId: 'step-123'
};

При этом стандартная типизация сохраняется.


Работа с generics и кастомной логикой

В Shepherd отсутствует встроенная поддержка generics для шагов, однако можно обернуть API:

type StepWithMeta<T> = Shepherd.StepOptions & {
  meta?: T;
};

const step: StepWithMeta<{ role: string }> = {
  text: 'Шаг',
  meta: {
    role: 'admin'
  }
};

Такой подход удобен для сложных сценариев.


Типизация экземпляра шага

Иногда требуется доступ к самому шагу:

const step: Shepherd.Step = tour.addStep({
  id: 'step'
});

Методы:

step.show();
step.hide();
step.cancel();

TypeScript предоставляет автодополнение и проверку методов.


Работа с DOM и строгая проверка

При использовании селекторов важно учитывать возможность null:

const el = document.querySelector('.item');

if (el) {
  tour.addStep({
    attachTo: {
      element: el,
      on: 'top'
    }
  });
}

Или с приведением типа:

element: document.querySelector('.item') as HTMLElement

Первый вариант безопаснее.


Типизация асинхронной логики

В шагах можно использовать асинхронные действия:

buttons: [
  {
    text: 'Загрузить',
    async action() {
      await fetch('/api/data');
      this.next();
    }
  }
]

TypeScript корректно обрабатывает Promise.


Интеграция с фреймворками

React

В React:

import { useEffect } from 'react';

useEffect(() => {
  const tour = new Shepherd.Tour();

  tour.addStep({
    text: 'React шаг'
  });

  tour.start();
}, []);

Типизация сохраняется автоматически.


Vue

В Vue.js:

import { onMounted } from 'vue';

onMounted(() => {
  const tour = new Shepherd.Tour();
});

Проверка типов и защита от ошибок

TypeScript предотвращает распространенные ошибки:

Ошибка в позиции:

on: 'center' // Ошибка: недопустимое значение

Ошибка в кнопке:

action: () => {
  this.next(); // Ошибка: неверный контекст this
}

Решение — использовать function:

action() {
  this.next();
}

Рекомендации по строгой типизации

  • включение strict в tsconfig.json
  • избегание any
  • использование as только при необходимости
  • явное объявление типов для сложных структур
  • разделение конфигурации и логики

Пример полностью типизированного тура

import Shepherd from 'shepherd.js';

const tour: Shepherd.Tour = new Shepherd.Tour({
  useModalOverlay: true
});

tour.addStep({
  id: 'welcome',
  title: 'Добро пожаловать',
  text: 'Начало тура',
  buttons: [
    {
      text: 'Далее',
      action() {
        this.next();
      }
    }
  ]
});

tour.start();

Статическая типизация делает код предсказуемым, документированным и устойчивым к изменениям API.