Надоели «сюрпризы» JavaScript? Не понимаете, как можно жить без классов? Не приемлете идею прототипированного наследования, и вас просто трясет от отсутствия строгой типизации? А с другой стороны, без JavaScript сейчас ведь просто не обойтись… Беда. Но выход есть! Даже несколько!
Общеизвестный факт – чистый JavaScript вызывает претензии у многих разработчиков. Особенно много нареканий исходит от программистов, привыкших к языкам с классической объектной моделью, но, кроме непривычного ООП, есть и другие замечания, часть из которых, как это ни грустно, вполне обоснована. Попытки улучшить JavaScript или придумать что-нибудь ему на замену предпринимались довольно давно. Гиганты информационной индустрии Microsoft и Google предлагают свои варианты выхода из ситуации. Сегодня мы рассмотрим язык программирования TypeScript.
TypeScript – JavaScript от Microsoft
TypeScript – язык, позиционируемый как средство разработки веб-приложений, расширяющее возможности JavaScript. Он был представлен корпорацией Microsoft в 2012 году. Примечательна личность разработчика – Андерса Хейлсберга (Anders Hejlsberg), принимавшего деятельное участие в создании Turbo Pascal, Delphi и C#. В отличие от CoffeeSript, TypeScript является обратносовместимым с JavaScript. Он компилируется в JavaScript, после чего программу на TypeScript можно выполнять в любом современном браузере. Как и CoffeeSript, его вполне можно использовать совместно сплатформой Node.js.
Отличия TypeScript от JavaScript:
возможность явного определения типов (статическая типизация);
поддержка использования полноценных классов (как и традиционная объектно-ориентированная модель);
поддержка подключения модулей.
Подобные нововведения призваны повысить скорость разработки, читаемость, рефакторинг и повторное использование кода, дать возможность осуществлять поиск ошибок на этапе разработки икомпиляции, а также увеличить скорость выполнения программ. Хотя, если честно, последний абзац смотрится и вовсе как рекламный слоган. Давайте посмотрим, что там есть на самом деле. Хорошие новости – действительно, каждая программа JavaScript является корректной программой TypeScript. Более того, компилятор TypeScript выполняет только локальное преобразование файлов, не делает никаких переопределений переменных и не меняет их названия. Это позволяет писать код, максимально близкий к оригинальному JavaScript.
Node.js как TypeScript-компилятор
Ну, теперь за дело. TypeScript действительно нуждается в компиляции, и путей для нее ровно два – использовать Microsoft Visual Studio (от 2012) или соответствующий модуль платформы Node.js. Последним мы и воспользуемся для наших примеров. Сначала установим сам модуль:
npm install -g typescript
Пользоваться им просто. Создаем простой файл с TypeScript-кодом (hello.ts):
function greeter(person: string) { return "Hello, " + person; } console.log(greeter("TypeScript"));
Теперь компиляция. Набираем в консоли:
$ tsc hello.ts
Результатом будет файл hello.js:
function greeter(person) { return "Hello, " + person; } console.log(greeter("TypeScript"));
Не много различий, правда? Ну, теперь, когда у нас все работает, давайте разберемся с самим языком.
Аннотации типов
Что нам может предложить TypeScript? Прежде всего статическую типизацию, мы ее наблюдали в первом же примере:
function greeter(person: string){...}
Тип данных тут указан после имени аргумента. Если мы передадим функции неверный тип данных, при компиляции будет выдано предупреждение:
greeter(1);
Результат:
$ tsc hello.ts gr.ts(4,13): error TS2082: Supplied parameters do not match any signature of call target: Could not apply type 'string' to argument 1 which is of type 'number'. gr.ts(4,13): error TS2087: Could not select overload f or 'call' expression.
Можно указать тип возвращаемого значения:
function process(x: number, y: number): number { var sum = x + y; return sum; }
А можно вообще ничего не указывать – строгая типизация не является в TypeScript обязательной. Зато функции теперь можно задавать параметры по умолчанию и указывать необязательные аргументы:
function process(x =5, y?: number): number { var sum; if(!y){ sum = x; } else{ sum = x + y; } return sum; } console.log(process(2,6)); //6 console.log(process(2)); //2 console.log(process()); //5
Результаты компиляции:
function process(x, y) { if (typeof x === "undefined") { x = 5; } var sum; if (!y) { sum = x; } else { sum = x + y; } return sum; } console.log(process(2, 6)); //8 console.log(process(2)); //2 console.log(process()); //5
Больше всего тут приятно, что результирующий код на JavaScript получается простым и понятным. Правда, это только на простых конструкциях, а нам пора переходить к более сложным.
Классы! Настоящие классы!
Готовы поспорить, ради того все и затевалось. Да, в TypeScript существуют «нормальные» классы и «нормальное» наследование. Пример TypeScript-класса:
class Person { name: string; surname: string; private id: number; static title = "Example"; constructor (name: string, surname: string) { this.name = name; this.surname = surname; } setID (id) { this.id = id; } getFullName () { return this.name+" "+this.surname; } } console.log(Person.title+":"); // Example: var User = new Person("Kirill", "Sukhov"); console.log(User.name); // Kirill console.log(User.getFullName()); // Kirill Sukhov
Тут почти все, о чем мечтали сторонники «традиционного» ООП: поля, методы, конструктор. Имеются и модификаторы доступа – попытка получить значение User.id или установить его значение непосредственно, а не с помощью специальных методов класса Person, потерпит неудачу (модификатор public тоже есть, но его почти всегда можно опустить).
Статические поля и свойства также подчиняются привычным законам – то есть доступны без создания экземпляра класса.
При компиляции этого кода мы получим следующую, не очень сложную JavaScript-конструкцию:
var Person = (function () { function Person(name, surname) { this.name = name; this.surname = surname; } Person.prototype.setID = function (id) { this.id = id; }; Person.prototype.getFullName = function () { return this.name + " " + this.surname; }; Person.title = "Example"; return Person; })(); console.log(Person.title + ":"); // Example: var User = new Person("Kirill", "Sukhov"); console.log(User.name); console.log(User.getFullName());
Теперь наследование. Напишем еще один класс, расширяющий предыдущий:
class Member extends Person { band: string; constructor(name: string, surname: string, band: string){ super(name, surname); this.band = band; } getBand () { return this.band; } } var User = new Member("John", "Lennon", "The Beatles"); console.log(User.getFullName()); // John Lennon console.log(User.getBand()); // The Beatles
Мы добавили немного – название группы. В конструкторе мы методом super() вызываем родительский конструктор.
Можно переопределить родительский метод:
class Member extends Person { band: string; constructor(name: string, surname: string, band: string){ super(name, surname); this.band = band; } getBand () { return this.band; } getFullName() { return super.getFullName()+" From "+this.band; } } var User = new Member("John", "Lennon", "The Beatles"); console.log(User.getFullName()); // John Lennon from The Beatles
Интерфейсы
Да, кроме классов, в TypeScript существуют и эти полезные языковые конструкции. Ниже пример простого интерфейса и использующей его функции:
interface Band { name: string; state?: string; members: any; } function ShowBand(band: Band) { console.log(band.name); if(band.state){ console.log(band.state); } band.members.forEach( function(member){ console.log(member); }); } var obj = { name: "Focus", state: "nl", members: ["Thijs", "Jan", "Martin", "Hans"] } ShowBand(obj);
Тут интерфейс контролирует параметры объекта, передаваемого функции как аргумент. Знак вопроса после параметра id указывает на его необязательность. При компиляции кода в JavaScript интерфейс исчезает, он свое дело сделал:
function ShowBand(band) { console.log(band.name); if (band.state) { console.log(band.state); } band.members.forEach(function (member) { console.log(member); }); } var obj = { name: "Focus", state: "nl", members: ["Thijs", "Jan", "Martin", "Hans"] }; ShowBand(obj);
Модули
Модули в TypeScript организованы по стандартам CommonJS и EcmaScript6. Их задача – инкапсуляция бизнес-логики в отдельной конструкции со своим пространством имен… Хотя, что объяснять, что такое модули в конце статьи про Node.js? Лучше покажу на практике, как они реализованы в TypeScript.
module Say { export function Hello(text: string) { return "Hello " + text; } } console.log(Say.Hello("Module"));
Ничего не напоминает? Хотя что, собственно, напоминать, это не сходство, это тот же самый механизм, который мы используем, например, в Node.js. При компиляции получаем следующее:
var Say; (function (Say) { function Hello(text) { return "Hello " + text; } Say.Hello = Hello; })(Say || (Say = {})); console.log(Say.Hello("Module"));
Что еще?
Кроме разобранных нами языковых конструкций, TypeScript еще много чем может порадовать разработчика. Например, реализацией примесей (mixins), array-синтаксисом из EcmaScript 6, типом данных Generic. Очень динамично развивающаяся технология, поддерживаемая технологическим гигантом – даже если вы сторонник чистого JavaScript, не стоит обделять вниманием этот проект.
В следующей статье расскажем вам об альтернативе JavaScript от Google — языке программирования Dart.
Постоянные ссылки
При копировании ссылка на TeaM RSN обязательна!