Новости

26.11.2024

«Рецепты TypeScript»

«Рецепты TypeScript» Стефана Баумгартнера – книга из серии «книг рецептов» O’Reilly. Такое название серии объясняется структурой этих книг: вместо бесконечных теоретических выкладок, сквозь которые продираться не то что нелегко, а попросту скучно, авторы предлагают погружаться в материал через «рецепты» – примеры реальных задач, которые сопровождаются лаконичными и понятными решениями и их подробным обсуждением – того, как автор к ним пришёл и почему они являются действенными. В этот ряд «поваренных книг» вписывается и та, о которой мы поговорим сегодня.

TypeScript очень часто называют расширенной версией JavaScript (некоторые эксперты даже не согласны с тем, что TypeScript – самостоятельный язык программирования). Оба этих языка являются одними из самых популярных в мире веб-разработки и тесно связаны друг с другом. Между ними, однако, есть одно значимое различие: если в JavaScript типизация динамическая, то в TypeScript – статическая. Статическая типизация обеспечивает удобство совместной работы команды разработчиков над крупными фрагментами кода и делает эту работу более продуктивной, ведь благодаря статической типизации обнаруживать ошибки в процессе компиляции гораздо проще. И, так как основной фокус книги направлен на такие возможности TypeScript, которые являются своего рода надстройкой над возможностями языка JavaScript, без знания основ последнего читать эту книгу будет непросто. Вдобавок к этому, тема сложной симбиотической связи между двумя этими языками проходит через всю книгу красной нитью: в некоторых главах, например, обсуждаются функции JavaScript (хотя и через призму TypeScript).

Структура книги довольно прозрачна: бóльшая её часть посвящена фундаментальным вопросам TypeScript. Рассмотрев их и уяснив основные механизмы работы с этим языком программирования, вы перейдёте к самому интересному – задачам «со звёздочкой».

Так, первая треть книги (главы 1-4) посвящена основам TypeScript – в ней рассматриваются следующие темы:

  • настройка проекта – возможности конфигурации для различных сред выполнения языка, систем модулей и целевых платформ;
  • иерархия типов и их внутреннее устройство;
  • сужение и расширение типов в зависимости от контекста их использования;
  • система обобщённых типов – расширенные возможности TypeScript.

Из этого фундамента вырастает ядро книги (главы 5-8), в котором собраны решения и обсуждения задач, связанных с такими темами, как условные типы, типы литералов шаблонов строк, вариативные типы кортежей и вспомогательные типы, – их рассмотрение сопровождается описанием приёмов метапрограммирования, возможность применения которых и делает TypeScript самостоятельным языком, а не просто придатком JavaScript.

Наконец, изучив основные компоненты языка TypeScript и его системы типов, вы сможете перейти к рассмотрению более сложных вопросов, которым и посвящена заключительная часть книги:

  • определения внешних типов и способы использования встроенных определений типов;
  • фреймворк React – как он интегрирован в TypeScript и какие возможности он предоставляет;
  • двойственность классов в TypeScript;
  • методологии и паттерны разработки ваших собственных типов.

А теперь в двух словах.

Почему эта книга полезна:

  • она представляет собой как исчерпывающий учебник по Typescript, так и удобный справочник, в котором можно найти ответ на (практически) любой вопрос, касающийся TypeScript;
  • она научит вас находить решения проблем самостоятельно;
  • благодаря ей вы будете готовы к постоянно появляющимся нововведениям в Typescript, ведь акцент в «Рецептах» делается на долгосрочных аспектах языка.

Кому будет полезна эта книга:

  • тем, кто занимается веб-разработкой и задействован в работе над крупными проектами;
  • тем, кто имеет дело с базами данных и/или занимается бэкенд-разработкой;
  • тем, кто уже знаком с Javascript (и Typescript) и хочет углубить свои знания и понять, как применять их на практике;
  • и вообще всем, кто хочет упростить свою работу над веб-приложениями и сделать её более продуктивной.

Если это про вас, то вооружайтесь «Рецептами» — и вперёд!

Предлагаем ознакомиться с отрывком «Обобщение сигнатур функций»

Задача


У вас есть две функции, которые работают одинаково, но с разными и в значительной степени несовместимыми типами.

Решение


Обобщите их поведение, используя обобщенные типы.

Обсуждение


Вы пишете приложение, которое хранит в объекте несколько языковых файлов (например, субтитры). Ключи — это коды языков, а значения — URL. Вы загружаете языковые файлы, выбирая их с помощью кода языка, который поступает из какого-либо API или пользовательского интерфейса в виде строки. Чтобы убедиться, что код языка правильный и допустимый, вы добавляете функцию isLanguageAvailable, которая выполняет входную проверку и устанавливает правильный тип с помощью предиката типа:

type Languages = {
de: URL;
en: URL;
pt: URL;
es: URL;
fr: URL;
ja: URL;
};

function isLanguageAvailable(
collection: Languages,
lang: string
): lang is keyof Languages {
return lang in collection;
}

function loadLanguage(collection: Languages, lang: string) {
if (isLanguageAvailable(collection, lang)) {
// lang — это ключ Languages
collection[lang]; // доступ есть!
}
}


То же приложение, другая ситуация, совершенно другой файл. Вы загружаете мультимедийные данные в HTML-элемент: либо аудио, либо видео, либо их комбинацию с определенными анимациями в элементе canvas. Все элементы уже есть в приложении, но вам нужно выбрать из них правильный, основываясь на входных данных, полученных от API. Опять же, выбор представляет собой строку, и вы пишете функцию isElementAllowed, чтобы убедиться, что входные данные действительно являются допустимым ключом вашей коллекции AllowedElements:

type AllowedElements = {
video: HTMLVideoElement;
audio: HTMLAudioElement;
canvas: HTMLCanvasElement;
};

function isElementAllowed(
collection: AllowedElements,
elem: string
): elem is keyof AllowedElements {
return elem in collection;
}

function selectElement(collection: AllowedElements, elem: string) {
if (isElementAllowed(collection, elem)) {
// elem — это ключ AllowedElements
collection[elem]; // доступ есть
}
}


Вы можете увидеть, что оба сценария очень похожи. Особенно это касается функций защиты типа. Если убрать всю информацию о типах и выровнять имена, то они будут идентичными:

function isAvailable(obj, key) {
return key in obj;
}


Два из них существуют благодаря информации о типе, которую мы получаем. Не из-за входных параметров, а из-за предикатов типа. В обоих сценариях мы можем узнать больше о входных параметрах, утверждая определенный тип keyof.

Проблема в том, что оба типа входных данных для коллекции совершенно разные и никак не пересекаются, за исключением пустого объекта, для которого мы не получим столько ценной информации, если создадим тип keyof. А keyof {} — это на самом деле never.

Но в коде есть информация о типе, которую мы можем обобщить. Мы знаем, что первый входной параметр — это объект, а второй — ключ свойства. Если эта проверка завершается значением true, то мы понимаем, что первый параметр является ключом второго.

Чтобы обобщить эту функцию, мы можем добавить к isAvailable параметр обобщенного типа Obj, заключенный в угловые скобки. Это заполнитель для фактического типа, который будет заменен при добавлении isAvailable. Мы можем использовать данный параметр так же, как мы использовали бы AllowedElements или Languages, и можем добавить предикат типа. Поскольку Obj может быть заменен любым типом, то key должен содержать все возможные ключи свойств — string, symbol и number:

function isAvailable<Obj>(
obj: Obj,
key: string | number | symbol
): key is keyof Obj {
return key in obj;
}

function loadLanguage(collection: Languages, lang: string) {
if (isAvailable(collection, lang)) {
// lang — это ключ Languages
collection[lang]; // доступ есть!
}
}

function selectElement(collection: AllowedElements, elem: string) {
if (isAvailable(collection, elem)) {
// elem — это ключ AllowedElements
collection[elem]; // доступ есть!
}
}


Вот и все: одна функция, которая работает в обоих сценариях, независимо от того, какие типы мы заменяем на Obj. Точно так же, как работает JavaScript! Мы по-прежнему получаем ту же функциональность и правильную информацию о типе. Доступ к индексам становится безопасным без ущерба для гибкости.

Что самое интересное? Мы можем использовать isAvailable точно так же, как мы использовали бы нетипизированный эквивалент JavaScript. Это связано с тем, что TypeScript выводит типы для параметров обобщенного типа в процессе использования. А это сопровождается некоторыми интересными побочными эффектами. Более подробную информацию об этом вы можете прочитать в рецепте 4.3.

 

Об авторе
image
Стефан Баумгартнер — разработчик и архитектор, живущий в Австрии. Является автором книги TypeScript in 50 Lessons и ведет популярный блог о TypeScript. В свободное время он организует встречи и конференции — например, стал инициатором встречи Rust Linz и Европейской конференции по TypeScript. Также является независимым консультантом по Rust и TypeScript на сайте oida.dev. Стефан любит итальянскую кухню, бельгийское пиво и британские виниловые пластинки.


Более подробно с книгой можно ознакомиться на сайте издательства:


Комментарии: 0

Пока нет комментариев


Оставить комментарий






CAPTCHAОбновить изображение

Наберите текст, изображённый на картинке

Все поля обязательны к заполнению.

Перед публикацией комментарии проходят модерацию.