Пейджер

🌍 Привет мир! 👋🏻

🌍 Привет мир! 👋🏻

Давно не было про TypeScript и сегодня важный пост, Generics 🎊! Если к вам придет понимание что это и зачем, значит ваше мышление в рамках типизации стало гораздо шире, а я верю что так и произойдет! 🎆

🥺 Мотивация

В разработке частенько нужно создавать универсальные функции и методы, которые работают с разными типами данных, для сохранения строгой типизации. И к слову, опять же, я стараюсь дать вам информацию, концепция которой кочует из одного языка программирования в другой. Generics в TypeScript весьма близки к реализации в C#, Java, Kotlin, Rust и Golang.

🟢 Пример

Все объяснения на фантиках 🎨 не дадут вам должного понимания, я делюсь своим практическим примером.

Представим, что у нас есть сервис, который запускает задачу на генерацию изображения. Задачи различаются не только исполнителем (сторонние сервисы по генерации изображений), но и набором параметров, которые нужно передать.

Чтобы избежать ошибок (которые могут возникнуть при неполной передаче необходимых параметров на вход метода) и сделать код надежным, используем Generics.

✔️ В первую очередь описываем типы параметров которые необходимы для каждой задачи в зависимости от исполнителя:

Параметры для задач через OpenAI:

export type OpenAIImageGenerationType = {
  contentId: string;
  buffer: Buffer;
  prompt: string;
};


Параметры для задач через DeepAI:

export type DeepAIImageGenerationType = {
  contentId: string;
  gender: GenderType;
  n: number;
};


✔️ Затем мы создаем словарь, связывающий тип исполнителя задачи с соответствующим набором параметров:

export enum GenerationExecutorTypeEnum {
  DEEP_IMAGE_GENERATION = 'deep_image_generation',
  OPEN_IMAGE_GENERATION = 'open_image_generation'
}

export type ExecutorToParamsMap = {
  [GenerationExecutorTypeEnum.OPEN_IMAGE_GENERATION]: OpenAIImageGenerationType;
  [GenerationExecutorTypeEnum.DEEP_IMAGE_GENERATION]: DeepAIImageGenerationType;
};


✔️ И теперь описываем тип задачи с помощью Generics, что позволяет нам автоматически определить набор параметров в зависимости от типа исполнителя:

export type GenerationJobType<E extends keyof ExecutorToParamsMap> = {
  name: string;
  executor: E;
  params: ExecutorToParamsMap[E];
  timeout?: number;
};


✔️ Вот как выглядит сервисный метод, который использует этот подход:

class JobImageGenerationService {
  async createGenerationJob<E extends keyof ExecutorToParamsMap>(
    incomingJob: GenerationJobType<E>
  ): Promise<void> {
    // Здесь строго типизированные параметры в зависимости от executor
    // Реализация задачи, которая зависит от переданного executor и params
  }
}


✔️ Ну и собственно пример вызова:

 await this.jobImageGenerationService.createGenerationJob({
    name: "generate_image,
    executor: GenerationExecutorTypeEnum.DEEP_IMAGE_GENERATION,
    params: {
      contentId,
      gender,
      n,
    }
});


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

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

💡 Вывод

Generics в TypeScript — это моща 💪, как только вы это поймете, будете в дамках. Удачи!

💬 Делитесь своим мнением в комментариях👇! Если вам понравилась статья, не забудьте поставить лайк и хорошего вам дня! 👍

#TYPESCRIPT
Медиа 1
Хотите больше таких постов?
Подпишитесь на канал и читайте продолжение в Telegram.
Подписаться на @ivanchikovitclub Открыть пост в Telegram