Проверка типов во 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *