TypeScript: общие впечатления

Думаю, многие из вас знают, что сейчас существует обилие различных языков (или инструментов), которые позволяют писать код компилируемый в JavaScript. Это CoffeeScript, Dart, GorillaScript и другие (довольно большой список можно найти здесь). Недавно я решил познакомиться с одним из представителей этого списка — языком программирования под названием TypeScript.

Я расскажу о личном опыте — почему решил попробовать именно его, как начать работать с ним, и какие выводы сделал, поработав с ним несколько дней.

Если вам это интересно — пожалуйста, заходите под кат.

Небольшая выжимка в качестве описания этого языка




Какие возможности открывает язык




Конечно, это далеко не все — это лишь основные моменты, которые я выделил.

Основные ссылки




Почему стоит попробовать TypeScript?


Размышляя о том, почему имеет смысл попробовать поработать на TypeScript, я выделил несколько основных пунктов:



С другой стороны, есть ряд минусов:



Начинаем работать


Перехожу к небольшим заметкам, которые я оставлял в уме по мере работы с TypeScript.

Сборка


Прежде всего, было необходимо написать сборщик тестового проекта. Есть несколько NPM пакетов для gulp которые позволяют компилировать TypeScript код в JavaScript. Без знания что выбрать я начал пробовать все пакеты в том порядке, в каком мне их выдал Google. Оказалось, что не все пакеты используют последнюю версию компилятора (последняя версия была 1.5.0), и из-за этого код который компилировался на сайте TypeScript (ссылка) не компилировался плагином для gulp. Методом проб и ошибок я остановился на пакете gulp-tsc, который поддерживает все версии компилятора и работает «на ура».

Компиляция


Каждый интерфейс, сигнатура каждого экспортированного метода: все это должно быть известно компилятору, иначе он откажется компилировать код. Я работал с AMD модулями (об этом чуть позже), и при импорте одних модулей в другие возникала проблема — компилятор совершенно ничего не знал об существовании других модулей.

Для этих целей существуют .d.ts файлы — файлы, в которых нужно определить что конкретно экспортирует тот или иной модуль, какие глобальные переменные и функции определены.

На первый взгляд — все просто. На деле здесь обнаружился подводный камень (смотрим код ниже).

Создадим файл foo.ts в котором определим модуль foo:

/// <amd-module name="foo" />

export = {
	bar: () => 'baz'
}


Создадим файл bar.ts в котором определим модуль bar, который импортирует модуль foo:

/// <amd-module name="bar" />

import foo = require('foo'); // Ошибка: "Cannot find external module 'foo'."

export = {
	foo: foo
}


Мы получили ошибку «Не удается найти внешний модуль foo.». Почему так происходит? Так происходит потому что этот модуль мы нигде не определили и компилятор про него не знает.

Создадим файл foo.d.ts, в котором расскажем компилятору, что есть такой модуль foo, и он экспортирует один метод bar:

declare module foo {
	export function bar(): string
}


Мы добавили определение модуля и теперь все похоже на правду, все должно заработать, не правда ли? Неправда, потому что ровным счетом ничего от этого не изменилось — компилятор по-прежнему не может найти модуль foo. Вопрос — почему?

Решение оказалось неожиданным — название модуля было определено не в кавычках.

Работающий код файла foo.d.ts:

declare module 'foo' {
	export function bar(): string
}


Едем дальше...

AMD


Конечно, если TypeScript дает возможность создавать AMD-модули и можно красиво импортировать зависимости, почему бы этим не воспользоваться хотя бы для теста?

Я попробовал — в TypeScript по-определению нельзя генерировать модули с именами. TypeScript дает возможность генерировать модули без имен, не более. Мне показалось, что это было бы странно, и оказалось, что это можно обойти.

Пример модуля, который будет скомпилирован в модуль с именем:

/// <amd-module name="foo" />

export = {
	bar: () => 'baz'
}


Что еще интересного?


Большая часть времени, проведенного за кодированием на TypeScript, не доставила проблем, но довольно часто возникают тонкие моменты, решение которых довольно трудно найти. Одна из таких проблем, которую я не смог решить на текущий момент — как описать объект, свойства которого будут динамически определяться (названия свойств неизвестны), но каждый из них должен содержать объект, который имеет строго определенный интерфейс?

Пример неработающего кода:

interface IBar {
	baz: string
}

var foo: {
	[property: string]: IBar
}

foo = {};

foo.foobar = {
	baz: 'Hi there!'
}

Пример работающего кода:

interface IBar {
	baz: string
}

var foo: {
	[property: string]: IBar
}

foo = {
	foobar: {
		baz: 'Hi there!'
	}	
};

Это, конечно, не все проблемы, которые возникли, но каждая из них носит именно такой характер, как в примере выше.

Небольшой список других проблем:


Небольшой итог


Для себя я отметил TypeScript как отличный инструмент, который позволит исключить некоторые риски (в основном, исключение возможности ошибки, и все проблемы из этого вытекающие), позволяет жестко описать взаимодействие между элементами приложения, предлагает базовые инструменты для работы с модулями/классами и компилируется не в бороду из кода, а в достаточно читаемый код.

P.S Очень вероятно, что некоторые из проблем возникли лишь потому, что я еще не умею «готовить» TypeScript и поэтому некоторая информация может быть не объективно точной, но даже в этом случае я надеюсь, что эта информация окажется кому-нибудь полезной.

P.P.S Буду рад комментариям от знатоков TypeScript.
@yakimchuk-ry
27.05.2015 22:29 UTC
Первоисточник