Универсальные машинописные тексты |

Привет, волшебники кодирования! Я хочу кратко написать об использовании Typescript и, в частности, об использовании дженериков при создании многоцелевых функций. Чем больше я играю с ними, тем сильнее я понимаю, что они сильнее, поэтому я хотел бы поделиться этой силой со всеми вами.

Так что же?

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

Вот базовая функция javascript, которая будет принимать массив значений и возвращать массив типа {value:any, label:string}, массив объектов с лучшими практиками при присвоении значений раскрывающемуся списку. Функция также будет сортировать массив, поэтому я назову ее formatAndSortList:

const formatAndSortList = (list, valueKey, labelKey) => {
  return list
    .map((item) => {
      const value =
        typeof valueKey === "function" ? valueKey(item) : item[valueKey];
      const label =
        typeof labelKey === "function" ? labelKey(item) : item[labelKey];
      return { value, label };
    })
    .sort((a, b) => (a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1));
};

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

interface User {
  userId: number;
  firstName: string;
  lastName: string;
}

interface Option {
  value: any;
  label: string;
}

const formatAndSortList = (
  list: any[],
  valueKey: any | ((item: any) => any),
  labelKey: string | ((item: any) => string)
): Option[] => {
  return list
    .map((item) => {
      const value =
        typeof valueKey === "function" ? valueKey(item) : item[valueKey];
      const label =
        typeof labelKey === "function"
          ? labelKey(item)
          : (item[labelKey] as string);
      return { value, label };
    })
    .sort((a, b) => (a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1));
};

const users: User[] = [
  { userId: 1, firstName: "Sean", lastName: "Hurwitz" },
  { userId: 2, firstName: "James", lastName: "Baxter" },
  { userId: 3, firstName: "Melody", lastName: "Brain" },
  { userId: 4, firstName: "Nelson", lastName: "Mandela" },
];

const formattedUsers = formatAndSortList(
  users,
  "userId",
  (user) => `${user.firstName} ${user.lastName}`
);

console.log("formattedUsers", formattedUsers);


Это работает, отлично, но что, если я попробую это:

const formattedUsers = formatAndSortList(
  users,
  "userId",
  (user) => `${user.names.firstName} ${user.names.lastName}`
);

Я получил бы ошибку времени выполнения, потому что names объект не существует. Так что набор текста не так совершенен. Как сообщить функции, какой именно массив я использую?

Дженерики спешат на помощь!

Вот измененная функция:

const formatAndSortList = <Type extends { [x: string]: any }>(
  list: Type[],
  valueKey: keyof Type | ((item: Type) => any),
  labelKey: keyof Type | ((item: Type) => string)
): Option[] => {
  return list
    .map((item) => {
      const value =
        typeof valueKey === "function" ? valueKey(item) : item[valueKey];
      const label =
        typeof labelKey === "function"
          ? labelKey(item)
          : (item[labelKey] as string);
      return { value, label };
    })
    .sort((a, b) => (a.label.toLowerCase() < b.label.toLowerCase() ? -1 : 1));
};

Мы объявляем <Type> generic перед нашим списком аргументов в круглых скобках, а затем мы можем ввести наш список аргументов, используя его. Примечание: переменная Type можно назвать как угодно

Теперь у вас есть отличный intellisense (по крайней мере, в VSCode) для вашей функции:

Скриншот 2022-12-18 в 07.26.52.png

Скриншот 2022-12-18 в 07.27.35.png

потому что Typescript знает, так как вы прошли через массив типа User для первого аргумента, что Type отображается как User и может быть идентифицирован в следующих двух аргументах на лету. Как по волшебству!

Так что, если вы попробуете неприятный трюк выше:

Скриншот 2022-12-18 в 07.29.40.png
Вы получите компилятор ошибка вместо этого избавит вас от многих разочаровывающих белых экранов в ваших проектах!

Обобщения также работают при описании интерфейсов. Например:

interface User {
  userId: number;
  firstName: string;
  lastName: string;
}

interface Project {
  id: string;
  name: string;
}

interface Pagination {
  take: number;
  skip: number;
  total: number;
}

interface UsersPaged {
  pagination: Pagination;
  data: User[];
}

interface ProjectsPaged {
  pagination: Pagination;
  data: Project[];
}

В идеале я хотел бы иметь полезную нагрузку разбивки на страницы для каждой схемы в моем проекте. Трудно создать {Schema}Paged interface каждый раз. Вместо этого я могу иметь общую схему:

interface PaginationSchema<Schema> {
  pagination: Pagination;
  data: Schema[];
}

И вызовите его как таковой:

const usersPaged: PaginationSchema<User> = {
  pagination: { take: 1, skip: 0, total: 1 },
  data: [{ userId: 3, firstName: "s", lastName: "h" }],
};

Заключение

Иногда я просто сижу в благоговении перед силой в моих руках. Это как раз то, в чем заключается радость кодирования. Надеюсь, вам понравилось это введение в дженерики, и я надеюсь, что вы попробуете его в своем следующем проекте! Дайте мне знать, что вы думаете в комментариях ниже.

До следующего раза, продолжайте кодить!

~ Шон

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *