Валидация свойств

В Haunted, как и в других веб-компонентных библиотеках, свойства компонентов (props) играют ключевую роль в передаче данных и управлении состоянием. Правильная валидация свойств обеспечивает предсказуемое поведение компонентов, облегчает отладку и предотвращает ошибки в рантайме.


Определение свойств

Свойства в Haunted объявляются через функцию component, используя стандартные свойства веб-компонентов. Наиболее распространённый способ — через объект с метаданными для каждого свойства:

import { component, html } from 'haunted';

const MyComponent = ({ title, count }) => html`
  <h1>${title}</h1>
  <p>Количество: ${count}</p>
`;

MyComponent.props = {
  title: { type: String, default: 'Заголовок' },
  count: { type: Number, default: 0 }
};

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

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

  • type задаёт ожидаемый тип свойства (String, Number, Boolean, Array, Object).
  • default определяет значение по умолчанию, которое используется при отсутствии внешнего значения.
  • Если тип свойства не совпадает с переданным значением, Haunted не вызывает ошибку, но результат может быть неожиданным. Поэтому важно следить за правильной типизацией.

Проверка типов

Haunted не выполняет строгую проверку типов автоматически. Для обеспечения типовой безопасности используется ручная проверка внутри компонента:

const MyComponent = ({ title, count }) => {
  if (typeof title !== 'string') {
    throw new TypeError(`Ожидался тип String для свойства title, получено ${typeof title}`);
  }
  if (typeof count !== 'number') {
    throw new TypeError(`Ожидался тип Number для свойства count, получено ${typeof count}`);
  }

  return html`
    <h1>${title}</h1>
    <p>Количество: ${count}</p>
  `;
};

Такой подход полезен при разработке сложных компонентов с динамическими входными данными.


Обработка обязательных и необязательных свойств

Свойства можно разделить на обязательные и необязательные. В Haunted отсутствует встроенный механизм обязательности, поэтому это реализуется через проверку значения или через дефолтное значение.

MyComponent.props = {
  title: { type: String },
  count: { type: Number, default: 0 }
};
  • title здесь является обязательным — отсутствие значения нужно проверять внутри функции компонента.
  • count — необязательное, автоматически получает значение 0.

Для проверки обязательных свойств можно использовать вспомогательную функцию:

function validateProps(props, requiredProps) {
  requiredProps.forEach(prop => {
    if (!(prop in props)) {
      throw new Error(`Свойство "${prop}" обязательно для компонента`);
    }
  });
}

const MyComponent = (props) => {
  validateProps(props, ['title']);
  return html`<h1>${props.title}</h1>`;
};

Кастомные валидаторы

Помимо проверки типов, часто требуется кастомная валидация, например проверка диапазонов чисел или формата строки:

const MyComponent = ({ age }) => {
  if (typeof age !== 'number' || age < 0 || age > 120) {
    throw new RangeError(`Возраст должен быть числом от 0 до 120, получено ${age}`);
  }
  return html`<p>Возраст: ${age}</p>`;
};

MyComponent.props = {
  age: { type: Number }
};

Кастомные валидаторы делают компоненты более надёжными и защищают от некорректного использования.


Свойства с функциями

Свойства в Haunted могут быть функциями. Их валидация сводится к проверке типа Function:

const ButtonComponent = ({ onClick }) => html`
  <button @click=${onClick}>Нажми меня</button>
`;

ButtonComponent.props = {
  onClick: { type: Function }
};
  • Проверка typeof onClick !== 'function' помогает избежать ошибок при попытке вызвать неопределённое свойство как функцию.

Валидация массивов и объектов

Для массивов и объектов важно не только проверить тип, но и структуру данных. Например:

const ListComponent = ({ items }) => {
  if (!Array.isArray(items)) {
    throw new TypeError(`Свойство "items" должно быть массивом`);
  }
  if (!items.every(item => typeof item === 'string')) {
    throw new TypeError(`Все элементы "items" должны быть строками`);
  }
  
  return html`<ul>${items.map(i => html`<li>${i}</li>`)}</ul>`;
};

ListComponent.props = {
  items: { type: Array, default: [] }
};

Это гарантирует предсказуемое отображение данных и упрощает работу с компонентом.


Автоматизация проверки

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

function withValidation(Component, schema) {
  return (props) => {
    Object.entries(schema).forEach(([key, { type, required }]) => {
      if (required && !(key in props)) {
        throw new Error(`Свойство "${key}" обязательно`);
      }
      if (props[key] != null && !(props[key] instanceof type)) {
        throw new TypeError(`Свойство "${key}" должно быть типа ${type.name}`);
      }
    });
    return Component(props);
  };
}

const SafeComponent = withValidation(MyComponent, {
  title: { type: String, required: true },
  count: { type: Number, required: false }
});

Такой подход позволяет централизованно управлять валидацией и уменьшает дублирование кода.


Практические рекомендации

  • Использовать props с указанием типа и значения по умолчанию для базовой безопасности.
  • Всегда проверять обязательные свойства внутри компонента.
  • Применять кастомные валидаторы для сложных типов данных или бизнес-логики.
  • При работе с массивами и объектами проверять не только тип, но и содержимое.
  • Для больших проектов использовать вспомогательные функции или обёртки для автоматизации проверки.

Эффективная валидация свойств в Haunted повышает стабильность компонентов, делает код более читаемым и упрощает масштабирование приложений.