Измените рабочий процесс разработки и устраните ошибки с помощью библиотеки io-ts.

Как разработчик в Elastic, у меня было немало проблем, когда дело доходит до кодирования и декодирования типов. Одним из инструментов, который помог оптимизировать мой рабочий процесс и устранить ошибки, является библиотека io-ts, которая предоставляет систему типа времени выполнения для декодирования/кодирования ввода-вывода.

Когда я впервые начал использовать эту библиотеку, это сбивало с толку, и я изо всех сил пытался понять ее концепцию, но, привыкнув к ней, я понял, насколько она полезна при работе над крупномасштабными проектами.

Вот почему в этом посте я рассмотрю преимущества использования io-ts для лучшего кодирования и декодирования типов и почему я рекомендую рассмотреть это каждому разработчику Typescript.

Что такое типы времени выполнения и чем они отличаются от типов Typescript?

Прежде чем углубляться в особенности io-ts, важно понять, что такое типы среды выполнения и чем они отличаются от типов TypeScript.

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

Существует несколько преимуществ использования типов времени выполнения с io-ts, в том числе:

  • Улучшенная безопасность типов: использование io-ts обеспечивает дополнительный уровень безопасности при кодировании и декодировании данных, предотвращая обработку неожиданных или недопустимых значений.
  • Повышенная гибкость. Типы среды выполнения допускают более сложные определения типов, чем типы TypeScript, что может быть полезно при работе со сложными структурами данных.
  • Простая интеграция с внешними источниками данных. С помощью io-ts легко определить собственные кодеки для интеграции с внешними источниками данных, такими как API или базы данных.
  • Более информативные сообщения об ошибках. При возникновении ошибки во время кодирования или декодирования io-ts предоставляет подробные сообщения об ошибках, которые упрощают выявление и устранение проблемы.

Однако есть и некоторые потенциальные недостатки использования io-ts, в том числе:

  • Повышенная сложность. С добавленной гибкостью типов среды выполнения возникает дополнительная сложность, которая может обескуражить разработчиков, не знакомых с библиотекой.
  • Накладные расходы. Использование типов среды выполнения может привести к дополнительным затратам во время кодирования и декодирования, что в некоторых случаях может повлиять на производительность.
  • Кривая обучения. Несмотря на то, что io-ts хорошо задокументирована, для освоения библиотеки по-прежнему требуется обучение.

Внедрение систем типа времени выполнения с помощью io-ts

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

Во-первых, мы определим типы среды выполнения для элементов нашего списка дел:

import * as t from 'io-ts';

const todoRT = t.type({
  id: t.number,
  title: t.string,
  completed: t.boolean
});

// 🤩 It is possible to derive a TS type from the runtime type definition
type Todo = t.TypeOf<typeof todoRT>;

const todoListRT = t.array(todoRT);

Определив эти типы среды выполнения, мы теперь можем использовать их для кодирования и декодирования данных. Например, чтобы декодировать данные JSON в элементы нашего списка дел, мы можем использовать следующий код:

import { pipe } from 'fp-ts/lib/function';
import * as E from 'fp-ts/lib/Either';
import * as D from 'io-ts/lib/Decoder';

const decodeTodos = (input: string) =>
  pipe(
    D.parse(input, todoListRT.decode),
    E.getOrElse(() => [])
  );

Используя функцию decodeTodos, мы можем легко декодировать строку JSON в массив элементов списка дел со встроенной обработкой ошибок.

Это базовый пример, но io-ts поставляется с утилитами, которые позволяют создавать более сложные типы среды выполнения!

Давайте теперь посмотрим на реальный пример создания общего типа среды выполнения для кодирования/декодирования полезной нагрузки ответа из CRUD API списков дел.

Использование типа времени выполнения Todo с API

Теперь, когда мы создали тип среды выполнения todoRT, давайте рассмотрим, как мы можем использовать его в более сложном примере. Теперь мы увидим, как кодировать полезную нагрузку ответа на запрос GET в бэкэнде, используя тип среды выполнения todoRT, и как декодировать ответ на клиенте.

Допустим, у нас есть конечная точка сервера, которая возвращает список задач в следующем формате:

interface Todo {
  id: number;
  title: string;
  description: string;
  completed: boolean;
  createdAt: string;
  updatedAt: string;
}

type TodoList = Todo[];

Как мы видели ранее, мы можем получить тип Typescript из определения типа времени выполнения, поэтому нам не нужно будет вручную создавать тип, определенный выше.

В бэкэнде мы можем использовать тип времени выполнения todoRT для проверки полезной нагрузки ответа перед отправкой обратно клиенту:

import * as t from 'io-ts';
import { isLeft } from "fp-ts/Either";

const todoRT = t.type({
  id: t.number,
  title: t.string,
  description: t.string,
  completed: t.boolean,
  createdAt: t.string,
  updatedAt: t.string
});

const todoListRT = t.array(todoRT);

type TodoList = t.TypeOf<typeof todoListRT>;

router.get('/api/todos', (req: Request, res: Response) => {
  // Fetch todos from the database
  const todos: TodoList = fetchTodosFromDb();

  // Validate the todos using the runtime type
  const result = todoListRT.decode(todos);

  if (isLeft(result)) {
    // Handle validation error
    res.status(500).send({ message: 'Invalid todo payload' });
    return;
  }

  // Return the validated todos
  res.status(200).send(result.right);
})

Функция isLeft из библиотеки fp-ts/Either принимает значение Either и возвращает логическое значение, указывающее, представляет ли оно левое значение или нет. Значение Left представляет случай сбоя или ошибки, а значение Right представляет случай успеха. Поэтому функцию isLeft можно использовать для проверки того, не привела ли операция к ошибке.

В клиенте мы можем использовать тип среды выполнения todoListRT для декодирования полезной нагрузки ответа перед ее использованием в нашем приложении:

async function getTodos() {
  const response = await fetch('/api/todos');
  const todos: TodoList = await response.json();

  // Validate the response using the runtime type
  const result = todoListRT.decode(todos);

  if (isLeft(result)) {
    // Handle validation error
    throw new Error('Invalid todo payload');
  }

  return result.right;
}

Сначала мы получаем ответ от сервера, а затем используем тип среды выполнения todoListRT для декодирования полезной нагрузки ответа. Если полезная нагрузка недействительна, мы выдаем ошибку. В противном случае мы возвращаем проверенные задачи.

Использование типов среды выполнения таким образом может увеличить нагрузку на ваше приложение, но оно того стоит, поскольку оно также может обеспечить лучшую безопасность типов и проверку данных вашего приложения.

Заключение

Я искренне верю, что мы всегда должны заботиться о проверке нашего ввода/вывода после передачи данных, и io-ts до сих пор был для меня отличным DX, я бы порекомендовал любому коллеге попробовать его в стороннем проекте или на сложная платформа, чтобы увидеть преимущества и структуру, которые она привносит в проект!

Дополнительные материалы на PlainEnglish.io.

Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter, LinkedIn, YouTube и Discord .