- Поддерживается транспиляция только typescript-файлов.
- Предполагается, что покрытие типами достаточно полное, в частности его достаточно для правильной подстановки соответствующих функций (например substr или array_slice - зависит от типа).
- Не поддерживаются асинхронные элементы и всё, что связано с event loop, т.к. в мире бэкенда нет соответствующих аналогов. В обозримом будущем может появиться поддержка получения данных по сети, как максимум.
- Не поддерживается и не будет поддерживаться взаимодействие с любыми браузерными API, в т.ч. DOM, WebWorkers, LocalStorage, etc, т.к. в мире бэкенда это не имеет смысла.
- Не поддерживается изменение переменных внешнего scope внутри замыканий.
- Не поддерживается
this
в произвольных местах, поддерживается только внутри простых классов: все остальное затранспилируется, но работать не будет. - Не поддерживаются сложные классы и прототипы.
- Не поддерживаются commonjs require конструкции (используем import/export вместо них).
Дополнительные особенности:
- Экспортируемые переменные (export const) должны иметь явный инициализатор, чтобы можно было однозначно вывести их тип, это необходимо для внутренних механик по пробросу их между модулями и внутри модуля.
- Переменные, объявленные на уровне модуля, должны иметь явный инициализатор по той же причине.
На данный момент поддерживаем следующие инструкции и выражения:
- Операторы:
- Логические (в т.ч. с присваиванием)
- Арифметические (в т.ч. с присваиванием)
- Тернарный оператор
- 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 внутри методов класса
- Статические свойства и обращение к ним снаружи и внутри класса
- Импортирование и экспортирование классов
Неподдерживаемые элементы синтаксиса, которые не имеют особого смысла (например, объявления типов и интерфейсов) молча игнорируются. Использование каких-либо элементов неподобающим образом сопровождается ошибкой в консоли - читайте внимательнее терминал.
Чтобы использовать подсказки при формировании типов в 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:
- Поддерживается только следующий синтаксис импорта:
import * as React from 'react';
- Для использования хуков можно использовать любой из двух способов (в области модуля, или в области функции-компонента):
const { useState: us } = React;
const st = React.useState;
- Поддерживаются только функциональные компоненты на основе react-hooks. Компоненты, основанные на классах, не поддерживаются.
- Предполагается, что изоморфные компоненты являются dumb-компонентами (в терминах smart/dumb компонентного подхода).
Все стандартные обработчики событий игнорируются при транспиляции.
- Чем однозначнее типы, которые вы используете - тем лучше, т.к. меньше шанс того, что 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);
. Обратите внимание, что тайпкасты в общем случае небезопасны, т.е. правильность их использования следует тщательно проверять. Другими словами, соблюдать строгое соответствие типа - это ваша ответственность.