Заключение
Заключение
Всё это была пара фактов
Книга получилась толстой, но держалась она на горстке утверждений, которые можно пересчитать по пальцам одной руки.
Первое мы поставили в самом начале и не отпускали: типы — это стираемый статический слой над неизменным динамическим рантаймом. Из него, как из единственной посылки, вывелось почти всё. Почему типов нет в рантайме. Почему instanceof Order невозможен, а instanceof Date рутинен. Почему catch (e: T) запрещён. Почему на границах нужна валидация, а на стыке с чужим миром гарантия компилятора испаряется. Половина «странностей» языка оказалась одним и тем же фактом, переодетым в разные костюмы.
Второе — структурная истина против номинального ярлыка. Тип не членский билет класса, а описание формы значения. Стоило это принять — и совместимость перестала спрашивать «кто твой родитель» и стала спрашивать «какая у тебя форма». Отсюда выросло всё в третьей части: объекты как формы, функции-граждане, классы, у которых private мягкий, потому что в структурном мире ему не на что опереться.
Третье — типы складываются, а не только классифицируют. Order — это не коробка с полями, а множество значений; "created" | "paid" | "shipped" — сумма, которой у номинативного ООП нет вовсе. Размеченные объединения (discriminated unions, объединения с общим полем-меткой) заменили половину полиморфизма, проверка исчерпанности через never поймала забытую ветку, сужение (narrowing, уточнение типа по ходу потока управления) превратило if в инструмент типизации. Алгебра вместо иерархии.
Четвёртое — система типов сама по себе язык. Дженерики оказались не ограниченным параметрическим полиморфизмом, а функциями, чьи аргументы — типы. Условные типы (conditional types, if на уровне типов) с infer — сопоставлением с образцом. Сопоставленные типы (mapped types, преобразование формы поле за полем) — циклом. Шаблонные литеральные типы — манипуляцией строками. Утилитные Partial, Pick, Record собрались из этих примитивов без всякой магии, а на дне обнаружился предел рекурсии — место, где язык говорит «дальше нельзя» и где дальше не стоит лезть самому.
Пятое — граница рантайма принадлежит тебе. Там, где tsc лжёт — any, as, JSON из сети, ответ платёжного шлюза, — гарантию держит не аннотация, а настоящая проверка: type guard (пользовательский предикат, сужающий тип в рантайме) или схема, которая инвертирует зависимость и сама порождает тип. Несостоятельность (unsoundness, сознательная готовность пропустить неверную программу) — не баг, а цена за то, чтобы натянуть типы на живой чужой код.
Вот и весь хребет. Структурность, стираемость, алгебра, вычисления на типах, дисциплина на границе. Удовольствие, которое, надеюсь, осталось у вас к этой странице, — не от того, что фактов было много, а ровно от обратного: их было мало, и всё остальное из них следовало. Язык, который сначала выглядел мешком разрозненных причуд, оказался выводимым из своих посылок. Это и есть момент, когда инструмент становится виден как парадигма.
Куда движется язык — и почему это подтверждает книгу
Самое сильное подтверждение тезиса пришло не из книги, а из самой траектории языка — и уже после того, как тезис был сформулирован.
В начале мы показали, что современный Node умеет запускать .ts напрямую, отрывая типы и не проверяя остаток. Тогда это был курьёз, иллюстрация к стиранию. Сейчас это направление, в котором движется вся экосистема. Node сделал стирание типов поведением по умолчанию. У tsc появился флаг --erasableSyntaxOnly, который запрещает писать конструкции, не стирающиеся подчистую, — enum, namespace с рантайм-телом, параметры-свойства в конструкторе. То есть язык начал официально вычищать те свои углы, где тип всё-таки протекал в рантайм.
// при --erasableSyntaxOnly:
enum Status { Created, Paid }
// ошибка: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.
// enum компилируется в реальный объект — он не стирается, а значит, запрещён
А над этим стоит предложение TC39 «типы как комментарии» (type annotations as comments): просьба к самому JavaScript разрешить синтаксис аннотаций и игнорировать его на уровне движка — ровно так, как движок игнорирует комментарий. Если оно пройдёт, .ts будет запускаться без всякого шага компиляции, потому что типы для рантайма станут пробелом по определению.
Сложите это вместе. Индустрия не отступает от стираемости — она её бетонирует. Делает абсолютной то, что в начале книги мы назвали несущей стеной. Организующая ось всего повествования — «типы стираются, рантайм их не помнит» — это ровно то, на что язык поставил своё будущее.
И обратите внимание, куда при этом ушла потребность в рантайм-типах, которая у джависта вызывала ломку. Она не исчезла — она переехала в схемы. Когда нужно, чтобы форма была реальна в рантайме, её описывают через Zod или Effect Schema, и тип выводят из схемы, а не наоборот. Часть про границу рантайма оказалась не обходным манёвром вокруг слабости языка, а тем местом, где язык осознанно провёл черту: статическое — компилятору, рантаймовое — схеме, и никакого смешения.
Честно: это ставка с разменом, а не триумф. За абсолютную стираемость платят тем, что рантайм навсегда остаётся слепым к типам, и дисциплину на границе с тебя уже никто не снимет. Но ставка сделана осознанно и сделана в ту же сторону, в какую смотрела книга. Приятно, когда будущее предмета подтверждает оптику, через которую ты его разбирал.
TypeScript как философия, а не инструмент
Теперь можно вернуться к рамке, с которой всё началось, — и она наконец заслужена, а не объявлена.
Почти все учат TypeScript как «JavaScript, к которому приделали аннотации ради безопасности». После этой книги видно, почему такое описание не просто бедное, а ложное по сути. «Аннотации ради безопасности» предполагают рантайм-охранника — а его нет, и вы убедились в этом на скомпилированном файле в первой же главе. Описание промахивается мимо самого механизма.
То, что осталось, когда промах убран, — связная парадигма, и её можно произнести одной длинной фразой. Думать формами значений, а не таксономией классов. Описывать данные суммами и произведениями, а не иерархиями наследования. Доказывать утверждения о форме статически, отдельной программой, которая стирается до запуска. Оборонять границу рантайма руками там, где доказательство не дотягивается. Программировать на самих типах, когда форма данных сложна настолько, что заслуживает вычисления. И калибровать строгость по месту — не максимум везде, а столько, сколько стоит держать на каждой конкретной границе.
Важно, что эти пункты не набор приёмов, а следствия друг друга. Стираемость заставляет оборонять границу. Структурность делает алгебру значений естественной. Алгебра значений делает вычисления на типах осмысленными, потому что вычислять есть что. Калиброванная строгость — прямой вывод из того, что несостоятельность принята осознанно: если дыры в системе по дизайну, вопрос не «есть ли они», а «где ты согласен их терпеть». Философия внутренне непротиворечива и вытекает из своих посылок — этим она и отличается от списка фич.
Здесь та «влюблённость», которую обещала концепция, разрешается во что-то более прочное. Не в восторг — восторг выветривается. В глубокое понимание: способность смотреть на незнакомую конструкцию языка и заранее знать, из какой посылки она выросла и какой размен за ней стоит. Это понимание не отнимешь, и устаревает оно куда медленнее синтаксиса.
Что значит думать формами
Подзаголовок книги был обещанием, и пора сказать, что именно оно означало, теперь, когда обещание выполнено.
Изменилось то, как вы видите значение. Раньше Order был коробкой с полями — местом, куда сложены id, total, status. Теперь это множество: все объекты, чья форма подходит, и ни одного лишнего. Раньше состояние заказа было полем со строкой и горсткой констант, которые надо не перепутать. Теперь это сумма — "created" | "paid" | "shipped" | "delivered" | "cancelled", — и забытая ветка не доживает до рантайма, потому что never ловит её при проверке. Раньше граница с внешними данными была местом, где «ну, наверное, придёт что надо». Теперь это место вашей ответственности, отмеченное явно, с проверкой, а не с верой. Раньше компилятор был придирой, который мешает писать. Теперь это ассистент доказательств, которому вы выдвигаете тезис и который добросовестно ищет в нём противоречие.
Это способ видеть, а не заученный синтаксис. Синтаксис прилагался бесплатно с первой страницы; перепрошить пришлось интуиции. Множества вместо коробок, суммы вместо флагов-перечислений, граница как точка ответственности, вывод вместо объявления, доказательство вместо охраны.
И — в характере этой книги — вкус, который вы выработали, калиброван, а не догматичен. Книга нигде не говорила «всегда аннотируй», «никогда не пиши any», «избегай классов». Она говорила: вот размен, вот его цена, плати её там, где она оправдана. Поэтому самый верный признак, что парадигма усвоена, — вы теперь способны осмысленно спорить с самой книгой. Сказать «здесь я возьму any сознательно, потому что граница дешёвая и риск нулевой» — и быть правым. Несогласие со знанием посылок дороже согласия без них.
Java, закрытая нить
Через всю книгу Java шла зеркалом, и пора честно закрыть эту нить.
Java не была злодеем и не была эталоном. Она была системой координат: новое объяснялось через знакомое, а знакомое временами вскрывалось как не настолько очевидное, каким казалось. Структурность против номинативности. Стирание дженериков — общий грех обоих языков, а не уникальный позор TS; джавист уже жил со стиранием в одном углу языка, TypeScript всего лишь распространил принцип на все типы. null как дыра против null, втянутого в систему типов. Дженерики, которые в одном языке — ограниченный полиморфизм, а в другом — полноценный язык вычислений.
Вывод из всех этих сравнений один, и он не в пользу ни одной из сторон. Ни один из языков не «лучше». Они стоят по разные стороны одной границы — стирание против рантайм-типов, структурность против номинативности, — и каждый по-своему связен. Рантайм Java помнит типы и платит за это весом и церемонией; рантайм JavaScript их забывает и платит дисциплиной на стыках. Это не шкала от плохого к хорошему. Это два разных размена, оба последовательные.
Поэтому вкус джависта, с которым он пришёл, не был неверным. Он был полным наполовину. «Тип реален в рантайме» — правда в Java и ложь здесь; «совместимость — это родословная» — правда в Java и ложь здесь. Теперь у вас в руках оба волокна, и вы держите их, зная, какое из чего. Это не отказ от старого мышления в пользу нового — это возможность выбирать между ними осознанно, под задачу. Беспристрастность, которой держалась глава об эстетике, держит и эту страницу: цель была не обратить джависта, а дать ему вторую систему координат и право выбирать.
Дальше
Книга заканчивается, язык — нет, и это правильный порядок вещей.
Никаких проводов с опекой. Самое полезное, что можно сделать дальше, лежит в открытом доступе и написано лучше любого упражнения: откройте .d.ts стандартной библиотеки и почитайте, как lib.es5.d.ts типизирует то, чем вы пользуетесь каждый день; откройте утилитные типы и проследите, как Pick и Record собраны из тех же примитивов, что вы теперь знаете; откройте реальную кодовую базу и посмотрите, где её авторы провели границу строгости и почему. И стройте своё — линза проверяется только на собственном коде.
Из всей книги в дорогу стоит взять две сквозные дисциплины. Знать, где остановиться: язык типов почти тьюрингов, и соблазн доказать на нём слишком многое ведёт в типы, которые никто не сможет прочесть. И калибровать: строгость — спектр, а не выключатель, и зрелость в том, чтобы выбирать её уровень на каждой границе, а не включать максимум из принципа.
Язык будет двигаться. Появятся флаги, уедет в стандарт предложение про аннотации-комментарии, экосистема схем переизобретёт себя ещё дважды. Но линза, которую вы собрали, переживёт эти движения, потому что собрана она не из фич, а из посылок, а посылки меняются куда реже синтаксиса. Теперь вы читаете это движение сами — и для каждой новой конструкции умеете спросить, из какого факта она выросла и чем за неё заплачено.
Тип — это не страж на воротах рантайма и не украшение над переменной. Это утверждение о форме данных, проверенное отдельной программой и стёртое до запуска. Думать формами — значит писать эти утверждения так, чтобы недопустимое не компилировалось, и точно знать, где кончается их власть. Этим вы теперь и владеете.