nemerleweb
Официальный русскоязычный блог проекта NemerleWeb www.nemerleweb.com
Типизация JS
22.06.2013
|
_NN_ |
Как оказалось тема не освещена совсем.
А между тем вопрос был поднят давно.
Используется очень просто:
Очевидно, что никакой типизацией и проверками ошибок не пахнет.
Создать юнит с именем window и методом alert не пройдет, т.к. NemerleWeb не обязан (и не генерирует) такой же код как должен быть для вызова JS.
Будет что-то вроде: window.alert["System.String"]("abc").
К примеру возьмем тот же window.alert:
Проще некуда.
Теперь мы можем писать в юните код на немерле и быть уверенным , что позовем правильно функцию.
Если метод возвращает не void, то нужно писать тупой код, который что-то возвращает.
Потом, мы не хотим вызывать этот код на стороне сервера, ведь он бессмыслен для него.
Ну и наконец у нас предупреждение о неиспользуемых переменных.
Итого надо писать что-то вроде:
Но можно упростить еще одним макросом JSApiImpl
Описываем методы, все остальное генерируется макросом.
Используется следующим образом:
Примеры ручного описания:
Типизация самих себя: nweb.js
Типизация 'undefined': undefined
Да, можно использовать обобщения в типизации
Отступление: парсеры я никогда не писал, однако это было несложно, что еще раз доказывает простоту и выразительность Nemerle.Peg.
Парсер создавался по спецификации и как оказалось в самом компиляторе TypeScript были баги, а все потому что они не пользовались генератором, а писали все вручную
У нас есть парсер для файлов описания JS (.d.ts): TSDeclarationParser.
По нему создаем AST и генерируем типы и переменные для JS.
Использовать совсем несложно:
Вот и все
Всю работу делает макрос.
Примеры использования сгенерированной типизации:
TreeNode.n
Скажем в JS можно писать такой код:
С одной стороны есть конструктор, с другой стороны можно вызвать просто F(0) и F("") и получить разные типы !
Как это типизировать ?
Чтобы покрыть этот случай используются специальные атрибуты и макросы.
Вот примерный код для типизации этого JS.
Использование
Сейчас там не все возможные библиотеки, но список пополняется по мере надобностей.
На данный момент парсер не поддерживает TypeScript 0.9 с генериками, постараемся доделать, чтобы не отставать от моды.
Теоретически можно убрать множество jsnew, jscall при генерации кода из TypeScript-а.
Ведь большинство людей не пишут такой вычурный код.
Но на это рук не хватает.
В TypeScript присутствует структурная типизация, которой конечно нет в Nemerle.
Очевидно нужно для одинаковых структурно типов генерировать одно и тоже имя, чтобы сделать код похожим на TypeScript.
Возможно это будет сделано в будущем.
Высказывайтесь, не стесняйтесь.
А между тем вопрос был поднят давно.
Предисловие
Итак простой способ вызова JS это макрос с очевидным названием 'js'.Используется очень просто:
[Unit]
class MyPage
{
public F() : void
{
js <# window.alert("abc"); #>
}
}
Очевидно, что никакой типизацией и проверками ошибок не пахнет.
Создать юнит с именем window и методом alert не пройдет, т.к. NemerleWeb не обязан (и не генерирует) такой же код как должен быть для вызова JS.
Будет что-то вроде: window.alert["System.String"]("abc").
Типизация JS
Для этой цели используется простой макрос JSApi:К примеру возьмем тот же window.alert:
[JSApi]
class window
{
public static alert(s : string) : void {}
}
Проще некуда.
Теперь мы можем писать в юните код на немерле и быть уверенным , что позовем правильно функцию.
[Unit]
class MyPage
{
public F() : void
{
window.alert("abc");
// window.alert(1) - нельзя, типизация однако !
}
}
Улучшения типизации
Проблемы:Если метод возвращает не void, то нужно писать тупой код, который что-то возвращает.
Потом, мы не хотим вызывать этот код на стороне сервера, ведь он бессмыслен для него.
Ну и наконец у нас предупреждение о неиспользуемых переменных.
Итого надо писать что-то вроде:
[JSApi]
class window
{
public static alert(s : string) : void
{
IgnoreParams();
throw ClientCodeCalledInServerException();
}
}
Но можно упростить еще одним макросом JSApiImpl
[JSApiImpl]
module window
{
public alert(s : string) : void;
}
Описываем методы, все остальное генерируется макросом.
JSApiRoot
Чтобы не захламлять глобальное пространство имен, однако иметь возможность все же генерировать JS в глобальном пространстве есть еще один макрос.Используется следующим образом:
[assembly: JSApiRoot(“MyNamespace”)]
namespace MyNamespace
{
module window
{
public alert(s : string) : void;
}
}
...
F() : void
{
MyNamespace.window.alert("abc"); // в JS будет: window.alert("abc");
}
Примеры ручного описания:
Типизация самих себя: nweb.js
Типизация 'undefined': undefined
Да, можно использовать обобщения в типизации
Генерация из файлов описания TypeScript
Ну а теперь феерическое завершение.Отступление: парсеры я никогда не писал, однако это было несложно, что еще раз доказывает простоту и выразительность Nemerle.Peg.
Парсер создавался по спецификации и как оказалось в самом компиляторе TypeScript были баги, а все потому что они не пользовались генератором, а писали все вручную
У нас есть парсер для файлов описания JS (.d.ts): TSDeclarationParser.
По нему создаем AST и генерируем типы и переменные для JS.
Использовать совсем несложно:
[assembly: GenerateTypedJS(
Root = "пространство имен куда все класть",
Lib = "Пусть к lib.d.ts, опционален”,
// Список кортежей
// Первый элемент папка откуда читать файлы.
// Второый элемент регулярное выражение, которое указывает, что нужно игнорировать
Files = [ ("TS", @"TS\\(_infrastructure|i18next\\lib)\\") ]
)]
Вот и все
Всю работу делает макрос.
Примеры использования сгенерированной типизации:
TreeNode.n
if(window.@event.ctrlKey)
_ = window.open("http://www.rsdn.ru" + this.Href, "_blank", null, false);
else
MainPage.Instance.SelectNode(this);
jscall и jsnew
Так уж получилось, что в JS система типов отличается от Nemerle.Скажем в JS можно писать такой код:
function F(a) {
if(this == window)
if(typeof a === "number" ) return 1;
else return "a";
else {
this.X = 1;
}
}
С одной стороны есть конструктор, с другой стороны можно вызвать просто F(0) и F("") и получить разные типы !
Как это типизировать ?
Чтобы покрыть этот случай используются специальные атрибуты и макросы.
Вот примерный код для типизации этого JS.
[JSApiImpl]
public interface F_X
{
public X : int { get; set; }
}
[JSApiImpl]
public interface F_Type
{
[JSCall]
public static @_(a : int) : int;
[JSCall]
public static @_(a : object) : String;
[JSNew]
public static @_() : F_X;
[JSNew]
public static @_(a : object) : F_X;
}
[assembly: JSApiRoot("M")]
module M
{
public F : F_Type;
}
Использование
using M;
[Unit]
class MyUnit
{
public X() : void
{
def a = jscall F(1); // var a = F(1);
def b = jscall F("x"); // var b = F(x);
def c = jsnew F(); // var c = new F();
def d = jsnew F(1); // var d = new F(1);
}
}
Заметки
Проект Nemerle.TypedJS предоставляет нужные библиотеки в типизированном виде.Сейчас там не все возможные библиотеки, но список пополняется по мере надобностей.
На данный момент парсер не поддерживает TypeScript 0.9 с генериками, постараемся доделать, чтобы не отставать от моды.
Теоретически можно убрать множество jsnew, jscall при генерации кода из TypeScript-а.
Ведь большинство людей не пишут такой вычурный код.
Но на это рук не хватает.
В TypeScript присутствует структурная типизация, которой конечно нет в Nemerle.
Очевидно нужно для одинаковых структурно типов генерировать одно и тоже имя, чтобы сделать код похожим на TypeScript.
Возможно это будет сделано в будущем.
Высказывайтесь, не стесняйтесь.
25.06.13 23:21: Ветка выделена из темы Типизация JS — VladD2
22.06.2013 0 комментариев |