Проверка типов во Vue при создании html. Меняем шаблоны на jsx

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Думаю, что сейчас необходимость статической типизации фронтенд приложений не вызывает сомнений, не буду описывать все ее плюсы и минусы, так как на Хабр уже неоднократно обсуждалась данная тема. В данной статье бы хотел предложить решение, которое позволит выполнять проверку типов при создании HTML разметки во Vue.

Какие проблемы существуют?

Типизация - это одна из главных проблем Vue.

В: Каковы самые слабые места Vue?

O: На данный момент, наверное, недружественность к типизации. Наш API разрабатывался без планирования поддержки типизированных языков (типа TypeScript), но мы сделали большие улучшения в 2.5.

из статьи Создатель Vue.js отвечает Хабру

Для типизации скрипта во Vue используется достаточно популярное решение - это библиотека vue-class-component, но вопросы по типизации шаблона так и остаются открытыми.

Какую типизацию для шаблонов хотелось бы получить?

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

  • Проверка используемых переменных

  • Проверка атрибутов для HTML элементов

  • Проверка событий(должен определяться тип для объекта события) для HTML элементов

  • Проверка параметров компонента

  • Проверка событий(должен определяться тип для объекта события) компонентов

  • Проверка слотов(должен определяться тип для параметров слота)

Поиск решения

Решений для типизации шаблонов я не нашел, поэтому стал рассматривать другие варианты.

Кроме шаблонов, Vue так же поддерживает render - функции и jsx. TypeScript поддерживает проверку типов для jsx, поэтому я решил подключить его в проект и посмотреть, что из этого получиться.

Подключение jsx позволило решить ряд проблем, но у компонентов не определялись параметры, слоты и события, исправить это мне помогла библиотека vue-tsx-support.

Подключение и использование данной библиотеки достаточно подробно описано у них на странице, поэтому сюда не буду дублировать эту информацию.

События во vue-tsx-support

Единственное, что мне не понравилось в данной библиотеке, так это работа с событиями. Вместо использования this.$emit они предлагают tsx.emitOn

import * as tsx from "vue-tsx-support";

tsx.emitOn(this, "onRowClicked", { item, index });

Проблема типизации событий связана с тем, что в jsx названия событий начинаются с префикса "on" (во vue создаем событие rowClicked, а в jsx подписываемся на onRowClicked)

В функции emitOn происходит преобразование названия события из jsx стиля к стандартному vue стилю("onRowClicked" => "rowClicked")

function emitOn<Events, Name extends string & keyof Events>(
  vm: Vue & { _tsx: DeclareOnEvents<Events> },
  name: Name,
  ...args: Parameters<EventHandler<Events[Name]>>
) {
  vm.$emit(
    name.replace(/^on[A-Z]/, v => v[2].toLowerCase()),
    ...args
  );
}

Решить проблему типизации событий можно и без данной функции. Для этого нужно переопределить типы для Component в библиотеке vue-tsx-support, добавив туда четвертый параметр VueEvents.

declare module "vue-tsx-support/lib/api" {
  interface Component<
    Props,
    PrefixedEvents = {},
    ScopedSlotArgs = {},
    VueEvents = {}
  > extends Vue {
    _tsx: TsxComponentTypeInfo<{}, Props, PrefixedEvents, {}>;
    $scopedSlots: InnerScopedSlots<ScopedSlotArgs>;
    $emit<T extends string & keyof VueEvents>(
      event: T,
      ...args: Parameters<EventHandler<VueEvents[T]>>
    ): this;
  }
}

Во VueEvents будут указываться названия событий в том виде, в котором они используются во vue(события в jsx стиле это PrefixedEvents).

Для того чтобы не делать отдельные интерфейсы для vue событий и jsx событий, я использую тип который выполняет данные преобразования(jsxEventMapper).

type ReverseMap<T extends Record<keyof T, keyof any>> = {
	[P in T[keyof T]]: {
		[K in keyof T]: T[K] extends P ? K : never;
	}[keyof T];
};

type ReverseJsxEvent<T> = ReverseMap<{ [key in keyof T & string]: `on${Capitalize<key>}` }>;

type jsxEventMapper<T> = {
	[rootKey in keyof ReverseJsxEvent<T>]: T[ReverseJsxEvent<T>[rootKey]];
};

В конечном итоге компонент выглядит следующим образом.

import * as tsx from 'vue-tsx-support';
import { VNode } from 'vue';
import Component from 'vue-class-component';
import { jsxEventMapper } from './jsxHelpers';

interface IProps {
	myProp1: string;
  myProp2: string;
}
interface IEvents {
	myEvent: number;
}
interface ISlots {
	mySlot: string;
}

@Component({})
export default class MyComponent extends tsx.Component<
	IProps,
	jsxEventMapper<IEvents>,
	ISlots,
	IEvents
> {
	private render(): VNode {}
}

IntelliSense в Visual Studio Code

Так же хотел бы отметить, что Visual Studio Code, отображает подсказки при работе с такими компонентами, что существенно улучшает процесс разработки.

Источник: https://habr.com/ru/post/651853/


Интересные статьи

Интересные статьи

В магазине Visual Studio есть множество различных расширений на все случаи жизни. Есть в сети различные их подборки, которые могут упростить жизнь в общих или конкретных случаях. Однако я, почему-то, ...
Нередко при работе с Bitrix24 REST API возникает необходимость быстро получить содержимое определенных полей всех элементов какого-то списка (например, лидов). Традиционн...
Всем привет. Когда я искал информацию о журналировании (аудите событий) в Bitrix, на Хабре не было ни чего, в остальном рунете кое что было, но кто же там найдёт? Для пополнения базы знаний...
Какой самый прочный материал на планете? Однозначного ответа на этот вопрос нет, поскольку все зависит от того, как именно вы будете оценивать прочность, и что вы подразумеваете под этим терм...
В «1С-Битрикс» считают: современный интернет-магазин должен быть визуально привлекательным, адаптированным для просмотра с мобильных устройств и максимально персонализированным с помощью технологии Бо...