Типизация императивных handlebars-шаблонизаторов в JSX через branded типы и кастомные J...
Типизация императивных handlebars-шаблонизаторов в JSX через branded типы и кастомные JSX-фабрики Переход с Handlebars на React часто оставляет за собой строковые шаблоны с {{#each}} и {{#if}}. Прямая передача таких строк в JSX ломает TypeScript, но есть способ это контролировать на уровне типов. Branded типы как контракт данных Branded тип - это строка с уникальным символом, которая не может быть создана без специальной фабрики. Обычная строка не скомпилируется в такой тип, что предотвращает случайную передачу сырого шаблона. type HandlebarsTemplate = string & { __brand: 'Handlebars' }; function hbs(strings: TemplateStringsArray, ...values: any[]): HandlebarsTemplate { return strings.reduce((acc, str, i) => acc + str + (values[i] || ''), '') as HandlebarsTemplate; } function jsx(tag: string, props: { template?: HandlebarsTemplate } | null, ...children: any[]) { if (tag === 'Template' && props?.template) { return compileAndRender(props.template); } return React.createElement(tag, props, ...children); } Типичная ошибка и её устранение Если передать сырую строку напрямую, TypeScript выдаст ошибку: // ❌ Ошибка: Type 'string' is not assignable to type 'HandlebarsTemplate' <Template template="<div>{{user}}</div>" /> Корректный вариант с фабрикой: const template = hbs<div>{{#if user}}Hello {{user.name}}{{/if}}</div>; function MyComponent() { return <Template template={template} />; } Production-oriented практика Чтобы кастомная JSX-фабрика работала, настрой Babel или TypeScript: добавь @jsx jsx в начале файла или укажи jsxFactory: 'jsx' в tsconfig. Для гибридных проектов это гарантирует, что ни один сырой шаблон не проскочит в рендер без компиляции. Если шаблон использует переменные контекста, добавь generics для типизации payload: function hbs<T>(strings: TemplateStringsArray, ...values: any[]): HandlebarsTemplate<T>; Trade-offs Дополнительный слой абстракции усложняет читаемость и увеличивает bundle, если шаблонов много. Используй такой подход только в пограничных сценариях миграции, а не как постоянную практику. Вывод: Branded типы и кастомные JSX-фабрики позволяют безопасно встраивать императивные шаблоны в декларативный JSX, предотвращая runtime-ошибки на этапе компиляции.