Используйте Typescript, чтобы убедиться, что ваш компонент используется правильно!

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

Когда мы думаем о React, на ум также приходит одна вещь — это Компоненты, т. е. многократно используемые фрагменты кода, они действительно важны для уменьшения количества вставляемых копий (взгляните на принцип DRY ), улучшите качество своего кода, а также облегчите работу своим коллегам-разработчикам. Для этого нам нужно сделать наш компонент максимально абстрактным, это поможет в обслуживании кода, а также улучшит удобство использования наших компонентов (принцип открытости-закрытости).

Теперь, когда я объяснил, почему это так интересно, давайте сделаем то, зачем мы сюда пришли!

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

type ComponentTableActionsProps = {
 action: 'edit' | 'delete' | 'edit-delete';
 deleteProps: DeleteActionProps;
 editProps: EditActionProps;
}
export default function ComponentTableActions({
  action,
  deleteProps,
  editProps,
}: ComponentTableActionsProps): JSX.Element {
  return (
     <>
       {(action.includes('edit') && editProps) && (
            <EditAction {...editProps} />)}
       {(action.includes('delete') && deleteProps) && (
            <DeleteAction {...deleteProps} />
       )}
     </>
  );
}

Этот компонент выше уже должен работать, но он еще не идеален. При его вызове требуется deleteProps, даже если вам нужна только кнопка редактирования, но это не поведение, которое нам нужно.

Чтобы решить эту проблему, мы можем немного изменить наш ComponentTableActionsProps, используя Discriminated Unions, мы можем добиться того, что ищем:

type ComponentTableActionsProps =
  | {
      action: 'edit';
      editProps: EditActionProps;
      deleteProps?: never;
    }
  | {
      action: 'delete';
      deleteProps: DeleteActionProps;
      editProps?: never;
    }
  | {
      action: 'edit-delete';
      editProps: EditActionProps;
      deleteProps: DeleteActionProps;
    };

И готово!

Но… Как это работает?

Отличный вопрос!

Мы говорим Typescript, что у нас есть объединение трех разных типов для нашего компонента, а именно:

  1. Когда действие равно 'edit', у нас также должен быть объект editProps, который будет передаваться в качестве реквизита, но НИКОГДА deleteProps (он нам не понадобится).
  2. Когда действие равно «удалить», нам нужен только deleteProps (так же, как и для редактирования).
  3. И, наконец, если действие равно 'edit-delete', то нам нужны ОБА editProps и deleteProps.

Вы можете спросить, почему я должен определять тип, который мне не нужен, как никогда, а не просто не передавать его? Это в основном из-за intellisense, если вы удалите эти строки, могут произойти две вещи: вы получите сообщение об ошибке всякий раз, когда вы вызываете этот компонент ИЛИ intellisense не распознает этот тип и не работает, когда вы пытаетесь CTRL+ПРОБЕЛ. Короче, оставь это там.

Вот и все для этой статьи, и я очень надеюсь, что вы узнали и получили удовольствие от ее чтения! Любые советы/советы приветствуются. Спасибо!