Skip to content

Latest commit

 

History

History
113 lines (90 loc) · 13.6 KB

support.md

File metadata and controls

113 lines (90 loc) · 13.6 KB

Поддержка синтаксиса и стандартной библиотеки

Общие ограничения в поддержке изоморфоного кода

  • Поддерживается транспиляция только typescript-файлов.
  • Предполагается, что покрытие типами достаточно полное, в частности его достаточно для правильной подстановки соответствующих функций (например substr или array_slice - зависит от типа).
  • Не поддерживаются асинхронные элементы и всё, что связано с event loop, т.к. в мире бэкенда нет соответствующих аналогов. В обозримом будущем может появиться поддержка получения данных по сети, как максимум.
  • Не поддерживается и не будет поддерживаться взаимодействие с любыми браузерными API, в т.ч. DOM, WebWorkers, LocalStorage, etc, т.к. в мире бэкенда это не имеет смысла.
  • Не поддерживается изменение переменных внешнего scope внутри замыканий.
  • Не поддерживается this в произвольных местах, поддерживается только внутри простых классов: все остальное затранспилируется, но работать не будет.
  • Не поддерживаются сложные классы и прототипы.
  • Не поддерживаются commonjs require конструкции (используем import/export вместо них).

Дополнительные особенности:

  • Экспортируемые переменные (export const) должны иметь явный инициализатор, чтобы можно было однозначно вывести их тип, это необходимо для внутренних механик по пробросу их между модулями и внутри модуля.
  • Переменные, объявленные на уровне модуля, должны иметь явный инициализатор по той же причине.

Typescript

На данный момент поддерживаем следующие инструкции и выражения:

  • Операторы:
    • Логические (в т.ч. с присваиванием)
    • Арифметические (в т.ч. с присваиванием)
    • Тернарный оператор
    • Rest-оператор, в том числе в параметрах функций
    • Spread-оператор, в том числе в параметрах функций
  • Условные конструкции - if/else, switch/case
  • Циклы - for, for-in, for-of, while, do-while
  • Optional chaining: ограниченная поддержка
    • Доступ к вложенным свойствам объекта - поддерживается.
    • Доступ к вложенным элементам массива по индексу - не поддерживается (возможно будет позже).
    • Доступ к вложенным свойствам объекта по вычисляемому - не поддерживается (возможно будет позже).
    • Вызов функции в середине цепочки - не поддерживается (и не будет).
    • Вызов вложенной функции в конце цепочки - на ваш страх и риск, т.к. существование функции в объекте нижнего уровня не проверяется.
    • Тем не менее в конце цепочки поддерживается вызов стандартных функций типа .toString(), .map()/.reduce() и им подобных.
  • Переменные - var, let, const
    • С уточнением, что переменные имеют функциональную область видимости, но не допускается их использование до инициализации. Другими словами, let и const семантически будут иметь смысл и попадать под соответствующие проверки только на стороне фронтенда.
  • Функции
    • Именованные объявления (например, function foo {})
    • Функциональные выражения (в том числе именованные, однако в выходном коде имена не сохраняются)
    • Стрелочные функции.
  • Template strings (кроме тегированных)
  • Enums:
    • базовая поддержка локальных и импортируемых enum-ов.
    • Приватные и вычислимые ключи не поддерживаются.
    • Enum должен быть либо полностью определен (каждый ключ имеет заданное значение), либо полностью неопределен (значения рассчитываются автоматически).
  • JSX
    • Встроенные элементы
    • Кастомные компоненты (глупые, но могут содержать некоторую вью-логику)
    • Фрагменты в сокращенной форме (<> </>)
  • Модули
    • Поддержка import (в т.ч. import * as ...)
    • Поддержка модификатора export для функций и переменных.
    • Поддержка алиасов в путях а-ля webpack.
  • Классы (базовая поддержка)
    • Только простейшие классы без наследования и без конструкторов
    • this внутри методов класса
    • Статические свойства и обращение к ним снаружи и внутри класса
    • Импортирование и экспортирование классов

Неподдерживаемые элементы синтаксиса, которые не имеют особого смысла (например, объявления типов и интерфейсов) молча игнорируются. Использование каких-либо элементов неподобающим образом сопровождается ошибкой в консоли - читайте внимательнее терминал.

Custom type hints

Чтобы использовать подсказки при формировании типов в phpdoc, подключите ./node_modules/@vkontakte/elephize/types в раздел typeRoots вашего tsconfig.json. После этого вы сможете использовать хинты int и mixed: const a: int = 1 и let b: mixed = getSomeMiscVars().

Условный рендеринг

Для разделения клиентской и серверной логики помимо директив ignoreImports и replaceImports в конфигурационном файле можно также использовать особую константу window._elephizeIsServer. Обратите внимание, что переменная работает только в составе тернарного оператора - это сделано намеренно, поскольку чем меньше будет разница - тем лучше для дальнейшей поддержки. Рекомендуется сочетать использование константы с конфигурацией через ignoreImports и replaceImports - игнорирование чисто клиентских импортов ускорит транспиляцию.

Как это работает?

Когда elephize встречает конструкцию вида window._elephizeIsServer ? serverCode : clientCode, он полностью игнорирует код clientCode и заменяет всё тернарное выражение целиком на результат транспиляции кода serverCode. Обратите внимание, что на клиентской стороне на данный момент нет возможности проигнорировать serverCode, т.е. нетранспилированный код, предназначенный для сервера, так или иначе попадет в клиентский бандл. Если это видится недопустимым, можно использовать константу window._elephizeIsServer совместно с заменой имплементации модуля при помощи конфигурационной директивы replaceImports.

Стандартная библиотека

На данный момент поддерживаем следующие элементы:

  • Math: все методы и константы.
  • String: includes, indexOf, join, slice, split (в т.ч. по регулярке), startsWith, substr, trim, replace (в т.ч. по регулярке, но поддерживаем только флаги /i и /g).
  • Array: filter, find, forEach, includes, indexOf, map, push, pop, reduce, slice, some, splice.
  • Object: hasOwnProperty.
  • Прочее: Object.keys, Object.values, Array.isArray, JSON.stringify, JSON.parse.

React

Ограничения на работу с React:

  • Поддерживается только следующий синтаксис импорта: import * as React from 'react';
  • Для использования хуков можно использовать любой из двух способов (в области модуля, или в области функции-компонента):
    • const { useState: us } = React;
    • const st = React.useState;
  • Поддерживаются только функциональные компоненты на основе react-hooks. Компоненты, основанные на классах, не поддерживаются.
  • Предполагается, что изоморфные компоненты являются dumb-компонентами (в терминах smart/dumb компонентного подхода).

Все стандартные обработчики событий игнорируются при транспиляции.

Ремарки про kphp

  • Чем однозначнее типы, которые вы используете - тем лучше, т.к. меньше шанс того, что kphp запутается при выводе типов в транспилированном коде.
  • kphp не умеет в union/intersection в общем случае, поэтому не стоит полагаться на union-типы в TS.
    • union скалярных типов точно будет работать.
    • union вида type | Array<...> точно НЕ будет работать.
    • также не будут работать union-типы из объектов, в том числе discriminated unions.
  • kphp требует, чтобы все типы были известны на этапе компиляции, поэтому в общем случае нельзя рекомендовать какой бы то ни было функциональный стиль написания кода. В частности, функции высшего порядка после транспиляции в php скорее всего не смогут быть корректно оттранслированы через kphp, за исключением самых простых случаев.
  • Обратите внимание на custom type hints - они могут сильно облегчить жизнь.
  • Поскольку исходный ts-код может быть слишком динамическим и согласовать его по типам может оказаться довольно сложно (часто случается так, что тип теряется на полпути, преобразуясь в mixed), добавлена возможность кастить переменные к нужному типу через комментарии особого вида: /* @elephizeTypecast TYPE */. В качестве TYPE могут быть следующие ключевые слова: array, int, float, string, boolean. Комментарий нужно разместить непосредственно перед идентификатором переменной. Пример: console.log(/* @elephizeTypecast array */ tca, /* @elephizeTypecast boolean */ tcb); будет преобразовано в \VK\Elephize\Builtins\Console::log((array) $tca, (bool) $tcb);. Обратите внимание, что тайпкасты в общем случае небезопасны, т.е. правильность их использования следует тщательно проверять. Другими словами, соблюдать строгое соответствие типа - это ваша ответственность.