Для разработчиков React это несложно, как вы думаете

Несколько лет назад я начал искать во всем простоту и минимализм. Это отразилось и на моей работе. Итак, когда началась «шумиха» над TypeScript, я просто закрылся от нее, потому что принятие ее казалось огромным делом и нарушило бы мой дзен. Пройдет год, чтобы попробовать это в большом корпоративном приложении, прежде чем все изменится. Оглядываясь назад, можно сказать, что несколько вещей помогли мне и моей команде принять TypeScript.

1. Понимание того, что на самом деле представляет собой TypeScript

Если вы прочитаете Руководство по TypeScript, неясно, где заканчивается JavaScript и начинается TypeScript. Таким образом, важно знать следующее.

Что такое TypeScript?

  • TypeScript добавляет статическую типизацию к обычному коду JavaScript. Ключевое слово здесь static, защиты во время выполнения нет. Типизированные объекты могут обновляться неправильными типами данных во время выполнения, ваш API может отправлять неправильные данные и вызывать сбой вашей страницы. Это также причина того, что довольно сложно вводить динамические типы в шаблонах функционального программирования.
  • Такие приятные функции, как деструктуризация, оператор распространения, async / await и т. Д., По-прежнему относятся к JavaScript, а точнее к ES2015. TypeScript позволяет использовать новейшие функции JavaScript, такие как необязательное связывание. По моему опыту, TypeScript очень регулярно следит за новыми функциями; намного лучше, чем вавилонская.

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

Нет ничего постыдного в использовании «any» или // @ ts-ignore

Некоторые части JavaScript сложно набирать, например, в Ramda compose можно связать до 6 функций. Внешние библиотеки могут создавать сумасшедшие проблемы с набором текста. Вы можете либо потратить много времени, пытаясь найти решение сложного типа, либо вы можете двигаться дальше и сосредоточиться на бизнес-логике. ИМО, написание кода должно быть связано с решением проблемы предметной области, а не с битьем головы о идиосинкразии языка / библиотеки, это крайне неэффективно, когда вам приходится часами гуглить, чтобы узнать, как ввести объект или функцию JavaScript. Просто выберите любой заполнитель. Решив проблему с доменом, вы можете вернуться к ней. Даже в этом случае важно сохранять простые типы. Следует избегать умного набора текста, убедитесь, что он может быть легко объяснен и понят новым членом команды.

2. Прекращение занятий

Я больше не могу писать код ООП. После того, как вас испортил JavaScript, где есть четкое разделение между данными и функциями, действительно сложно вернуться и начать писать код, в котором все является классом. Очень сложно разделить преобразования и составить поведения с помощью языка, основанного на классах, такого как Java. Кроме того, невозможность модульного тестирования частных функций также делает его неадекватным.

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

3. Основы

Лучший способ освоить TypeScript - начать с базового набора текста. Следующие примеры должны хорошо проиллюстрировать их:

Основные типы

const name: string = "Joe";
const count: number = 9;
const isActive: boolean = false;
const colors: string[] = ["red", "blue"];
enum Color { Red, Green, Blue };
let c: Color = Color.Green;

Комбинирование типов (Союз)

const zipcode: string | number;

Теперь почтовый индекс может быть строкой или числом, также полезно быстро определить подмножество допустимых значений.

type FlagColors = "red" | "white" | "blue";

Перечисления

Https://www.typescriptlang.org/docs/handbook/enums.html

Группа связанных констант должна быть определена в Enum:

export enum Action {
  SET_ITEM_LIST,
  SET_SELECTED_ROWS,
  SET_SELECTED_ITEM
}

4. Интерфейсы

Интерфейсы - отличная замена проптипам React, они позволяют самостоятельно документировать компоненты без использования шаблонов. Вы можете комбинировать это с деструктуризацией так:

interface ISelectPulldown {
  label: string;
  items: string[];
  tooltip?: string; // ? means that the prop is optional
}
const SelectPulldown = ({ label, items }: ISelectPulldown): void => { 
  // return component here
}
// or
const SelectPulldown: React.FC<ISelectPulldown> = ({ label, items }: ): void => { 
  //
}

Интерфейсы также могут помочь вам определить статические структуры данных, это объекты, имеющие определенную форму. Вот так:

interface Position {
  x: number;
  y: number;
}

Избегайте повторения с "расширениями"

Вы можете убрать общие свойства с помощью extends, t это полезно, когда у вас есть семейство компонентов, которое «наследует» набор базовых свойств.

interface IText {
  text: string;
}
interface INavLabel extends IText {
  selected?: boolean;
  visited?: boolean;
}

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

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

interface IWarningMessage extends React.HTMLProps<HTMLDivElement> {
  level: number;
}
export const WarningMessage: React.FC<IWarningMessage>
  = ({level, children, ...props}) => 
      <div className={level === 1 ? "error" : "warning"} {...props}>
          {children}
      </div>;

Теперь вы можете так называть компонент.

<WarningMessage data-testid="msg-1" level="1">
  This is an error
</WarningMessage>

Посмотрите, как можно отправить тестируемые данные, не определяя его как PropType?

5. Набор подписей функций

Когда дело доходит до функций, TypeScript слишком многословен. Встроенный ввод делает ваш код нечитаемым:

const myAdd: AddFunction: (x: number, y: number) => number = (x, y) => x + y;

Отделение определения типа от определения функции может добавить немного большей ясности.

type AddFunction = (x: number, y: number) => number;
const myAdd: AddFunction = (x, y) => x + y;

Это все еще слишком многословно для функции, которая раньше выглядела так:

const myAdd = (x, y) => x + y;

Но набор текста защитит ваши функции от неправильного использования.

6. Другие удобные функции TypeScript.

Время от времени могут пригодиться следующие функции.

тип

В крайнем случае, typeof пригодится для создания типа из уже инициализированного объекта.

const selectedCity = {
  id: "62396051",
  status: "Active",
  name: "East Creola",
  type: "City",
  alert: false,
  population: 6500,
  credName: ""
};
type ICity = typeof selectedCity;

Например, если вы используете Formik, вы можете создавать свои типы из самого initialValues.

ключ

Что, если я хочу создать тип, являющийся ключами объекта? Рассмотрим приведенный ниже пример. У меня есть жестко запрограммированный объект, который будет управлять этим типом. Когда я добавляю новый элемент, я не хочу ничего перепечатывать. Вы можете использовать утилиту keyof. Он очень удобен и может использоваться вместе с typeof.

const metal = {
  lead: { atomicNumber: 82 },
  mercury: { atomicNumber: 80 }
};
export type TemplateNames = keyof typeof templates;

Пропускать

В отличие от extends, Omit создает новый тип, опуская определенные свойства из интерфейса.

interface IAddress { 
  name: string; 
  street: string;
  poBox?: string;
  city: string;
}
type JustAddress = Omit<IAddress, "name">;

Частичное

Partial делает все свойства интерфейса необязательными, это помогает, если вы хотите обрабатывать один и тот же интерфейс по-разному в зависимости от ситуации.

let addr: Partial<IAddress> = {};
/* the object now becomes : { 
  name?: string; 
  street?: string;
  poBox?: string; 
  city?: string
} */

Только для чтения

Преобразует все свойства в интерфейсе только для чтения.

let addr: Readonly<IAddress> = {};
/* the object now becomes : { 
  name: string; 
  street: string;
  poBox: string; 
  city: string
} */

ReturnType

Создает тип на основе возвращаемого типа функции.

7. Дженерики

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

type StyledComponentProps<
  C extends keyof JSX.IntrinsicElements| React.ComponentType<any>,
  T extends object,
  O extends object,
  A extends keyof any
> =
    O extends object
     ? WithOptionalTheme<
      Omit<ReactDefaultizedProps<C, React.ComponentPropsWithRef<C>>    
       & O, A> &
      Partial<Pick<React.ComponentPropsWithRef<C> & O, A>>,T> &
      WithChildrenIfReactComponentClass<C>
     : never;

Дженерики - это типы, у которых есть заполнители. Для пояснения рассмотрим пример связанного списка.

interface LinkedNode<T> {
  next: LinkedNode<T> | null;
  data: <T>;
}
function addNode<T>(parent:LinkedNode<T>, data: T): LinkedNode<T> {
  const node: LinkedNode<T> = {
    next: null,
    data: data
  };
  parent.next = node;
  return node;
};

Теперь я могу создать связанный список, в котором могут храниться разные типы данных.

let node: LinkedNode<string> = getNode("New");
let numberNode: LinkedNode<number> = getNode(2);

Резюме

Три самые большие проблемы с TypeScript - это крутая кривая обучения, сложность ввода динамических / функциональных шаблонов и синтаксический шум. Однако он добавляет огромную ценность при создании большого приложения, которое можно расширять и поддерживать в течение длительного времени. Но помните, что TypeScript происходит из лагеря ООП, который плохо сочетается с функциональной природой React. HOC и некоторые динамические функциональные конструкции сложно напечатать. Будьте проще, тратить день на то, чтобы «красиво» что-то набрать, не стоит. Не работайте с инструментом, позвольте инструменту работать на вас.

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