Share This
Связаться со мной
Крути в низ
Categories
//12 советов по внедрению TypeScript в React-приложениях

12 советов по внедрению TypeScript в React-приложениях

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

12 sovetov po vnedreniju typescript v react prilozhenijah 0ade0b0 - 12 советов по внедрению TypeScript в React-приложениях

Зачем использовать TypeScript в React?

React – это JavaScript-библиотека с открытым исходным кодом для создания пользовательских интерфейсов, а TypeScript – типизированное надмножество JavaScript. Объединяя их, мы создаем пользовательские интерфейсы, используя типизированную версию JavaScript. Это означает большую безопасность и меньшее количество ошибок, отправляемых во внешний интерфейс.

Не существует единственного правильного способа написания кода React с использованием TypeScript. Как и в случае с другими технологиями, если ваш код компилируется и работает, вы, вероятно, что-то сделали правильно.

Подготовка компонентов к совместному использованию с помощью TypeScript

12 sovetov po vnedreniju typescript v react prilozhenijah aee4e10 - 12 советов по внедрению TypeScript в React-приложениях

просмотр общих компонентов React в bit.dev

Bit.dev стал популярной альтернативой традиционным библиотекам компонентов, поскольку он предлагает способ собрать и поделиться отдельными компонентами из любой кодовой базы.

Создавая проекты с использованием React с TS, вы убедитесь, что ваши компоненты легко понятны другим разработчикам. Это отличный способ написать поддерживаемый код и оптимизировать совместную работу команды.

Настройка проекта и tsconfig.json

Самый быстрый способ запустить приложение React/TypeScript – использовать create-react-app с шаблоном TypeScript:

         npx create-react-app my-app --template typescript     

tsconfig.json это файл конфигурации TypeScript. Файл содержит первоначальные настройки, ниже приведем несколько параметров с пояснениями:

tsconfig.json

         {    "compilerOptions": {      "target": "es5", // Укажите целевую версию ECMAScript      "lib": [        "dom",        "dom.iterable",        "esnext"      ], // Список файлов библиотеки для включения в компиляцию      "allowJs": true, // Разрешить компиляцию файлов JavaScript      "skipLibCheck": true, // Пропустить проверку типов всех файлов объявлений      "esModuleInterop": true, // Отключает импорт пространства имен (импорт * как fs из "fs") и включает импорт в стиле CJS / AMD / UMD (импорт fs из "fs")      "allowSyntheticDefaultImports": true, // Разрешить импорт по умолчанию из модулей без экспорта по умолчанию      "strict": true, // Включить все параметры строгой проверки типов      "forceConsistentCasingInFileNames": true, // Запрещаем ссылки с несогласованным регистром на один и тот же файл.      "module": "esnext", // Указываем генерацию кода модуля      "moduleResolution": "node", // Разрешить модули в стиле Node.js      "isolatedModules": true, // Безоговорочно генерировать импорт для неразрешенных файлов      "resolveJsonModule": true, // Включить модули, импортированные с расширением .json      "noEmit": true, // Не выводить вывод (то есть не компилировать код, а только выполнять проверку типа)      "jsx": "react", // Поддержка JSX в файлах .tsx      "sourceMap": true, // Создание соответствующего файла .map      "declaration": true, // Создаем соответствующий файл .d.ts      "noUnusedLocals": true, // Сообщать об ошибках на неиспользуемых локальных объектах      "noUnusedParameters": true, // Сообщаем об ошибках неиспользуемых параметров      "incremental": true, // Включить инкрементную компиляцию путем чтения/записи информации из предыдущих компиляций в файл на диске      "noFallthroughCasesInSwitch": true // Сообщать об ошибках для случаев падения в инструкции switch    },    "include": [      "src/**/*" // *** Файлы TypeScript должны ввести проверку ***    ],    "exclude": ["node_modules", "build"] //  *** Файлы, которые не нужно вводить, проверять ***  }     

Дополнительные рекомендации исходят от сообщества response-typescript-cheatsheet, а объяснения взяты из документации по параметрам компилятора в официальном справочнике TypeScript.

Enums

Enums определяет набор связанных констант как часть единой сущности.

         //...  /** A set of groupped constants */  enum SelectableButtonTypes {      Important = "important",      Optional = "optional",      Irrelevant = "irrelevant"  }  interface IButtonProps {      text: string,      /** The type of button, pulled from the Enum SelectableButtonTypes */      type: SelectableButtonTypes,      action: (selected: boolean) => void  }  const ExtendedSelectableButton = ({text, type, action}: IButtonProps) => {      let [selected, setSelected]  = useState(false)      return (<button className={"extendedSelectableButton " + type + (selected? " selected" : "")} onClick={ _ => {          setSelected(!selected)          action(selected)      }}>{text}</button>)  }  /** Exporting the component AND the Enum */  export { ExtendedSelectableButton, SelectableButtonTypes}     

Использование Enums:

         import React from 'react';  import './App.css';  import {ExtendedSelectableButton, SelectableButtonTypes} from './components/ExtendedSelectableButton/ExtendedSelectableButton'  const App = () => {    return (      <div className="App">        <header className="App-header">          <ExtendedSelectableButton type={SelectableButtonTypes.Important} text="Select me!!" action={ (selected) => {            console.log(selected)           }} />               </header>      </div>    );  }  export default App;     

Интерфейсы и типы

Что следует использовать – интерфейсы или псевдонимы типов? Хотя эти сущности концептуально различны, на деле они очень похожи:

  • обе могут быть продлены;
         //расширение интерфейсов  interface PartialPointX { x: number; }  interface Point extends PartialPointX { y: number; }  //расширение типов  type PartialPointX = { x: number; };  type Point = PartialPointX & { y: number; };  // Интерфейс расширяет тип   type PartialPointX = { x: number; };  interface Point extends PartialPointX { y: number; }  //Псевдоним типа расширяет интерфейсы  interface PartialPointX { x: number; }  type Point = PartialPointX & { y: number; };     
  • обе могут использоваться для определения формы объектов;
         //определяем интерфейс для объектов  interface Point {    x: number;    y: number;  }  //также используем типы  type Point2 = {    x: number;    y: number;  };     
  • обе они могут быть реализованы одинаково;
         //реализация интерфейса  class SomePoint implements Point {    x: 1;    y: 2;  }  //Реализация псевдонима типа  class SomePoint2 implements Point2 {    x: 1;    y: 2;  }  type PartialPoint = { x: number; } | { y: number; };  // Единственное, что вы не можете сделать: реализовать тип объединения  class SomePartialPoint implements PartialPoint {    x: 1;    y: 2;  }     

Единственная функция интерфейсов, которую не делают псевдонимы типов – это объединение деклараций.

Расширение элементов HTML

Иногда ваши компоненты работают как собственные HTML-элементы. В таких случаях лучше определить тип компонента как собственный элемент HTML или его расширение.

         function eventHandler(event: React.MouseEvent<HTMLAnchorElement>) {      console.log("TEST!")  }  const ExtendedSelectableButton = ({text, type, action}: IButtonProps) => {      let [selected, setSelected]  = useState(false)      return (<button className={"extendedSelectableButton " + type + (selected? " selected" : "")} onClick={eventHandler}>{text}</button>)  }     

Типы событий

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

         import React, { Component, MouseEvent } from 'react';     

Мы также можем использовать для ограничения элементов Generics с конкретным обработчиком событий.

Также можно применять объединения, чтобы разрешить повторное использование обработчика несколькими компонентами:

         / ** Это позволит вам использовать этот обработчик событий как для якорей, так и для элементов кнопок * /  function eventHandler(event: React.MouseEvent<HTMLAnchorElement | HTMLButtonElement>) {      console.log("TEST!")  }     

Определение интегрированного типа

Стоит упомянуть файлы index.d.ts и global.d.ts в React. Оба они устанавливаются, когда вы добавляете React в свой проект. Эти файлы содержат определения типов и интерфейсов: чтобы понять свойства или доступность конкретного типа, вы можете открыть эти файлы и просмотреть их содержимое.

12 sovetov po vnedreniju typescript v react prilozhenijah 2dd13d0 - 12 советов по внедрению TypeScript в React-приложениях

index.d.ts

Там вы увидете небольшой раздел файла index.d.ts, показывающий подписи для функции createElement.

ESLint/Prettier

Чтобы гарантировать, что ваш код соответствует правилам проекта, а стиль согласован, рекомендуется настроить ESLint и Prettier:

  • Установите необходимые зависимости для разработчиков:
         yarn add eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-react --dev     
  • Создайте файл .eslintrc.js в корне и добавьте следующее:

.eslintrc.js

         module.exports =  {    parser:  '@typescript-eslint/parser',  // Указывает парсер ESLint    extends:  [      'plugin:react/recommended',  // Использует рекомендуемые правила из @eslint-plugin-react      'plugin:@typescript-eslint/recommended',  // Использует рекомендуемые правила из @typescript-eslint/eslint-plugin    ],    parserOptions:  {    ecmaVersion:  2018,  //Позволяет анализировать современные функции ECMAScript    sourceType:  'module',  //Разрешает использование импорта    ecmaFeatures:  {      jsx:  true,  // Разрешает анализ JSX    },    },    rules:  {      // Место для указания правил ESLint. Может использоваться для перезаписи правил, указанных в расширенных конфигах      // например "@ typescript-eslint / явный-возвращаемый-тип-функции": "выкл.",    },    settings:  {      react:  {        version:  'detect',  // Указывает eslint-plugin-react автоматически определять версию React для использования      },    },  };     
  • Добавьте зависимости Prettier:
         yarn add prettier eslint-config-prettier eslint-plugin-prettier --dev     
  • Создайте файл .prettierrc.js в корне и добавьте в него следующий код:

.prettierrc.js

         module.exports =  {    semi:  true,    trailingComma:  'all',    singleQuote:  true,    printWidth:  120,    tabWidth:  4,  };     
  • Обновите файл .eslintrc.js:

.eslintrc.js

         module.exports =  {    parser:  '@typescript-eslint/parser',  // Задает парсер ESLint    extends:  [      'plugin:react/recommended',  // Использует рекомендуемые правила из @ eslint-plugin-react      'plugin:@typescript-eslint/recommended',  // Использует рекомендуемые правила из @typescript-eslint/eslint-plugin  +   'prettier/@typescript-eslint',  // Использует eslint-config-prettier для отключения правил ESLint из @typescript-eslint/eslint-plugin, которые будут конфликтовать с prettier  +   'plugin:prettier/recommended',  // Включает eslint-plugin-prettier и отображает более красивые ошибки как ошибки ESLint. Убедитесь, что это всегда последняя конфигурация в массиве extends.    ],    parserOptions:  {    ecmaVersion:  2018,  // Позволяет анализировать современные функции ECMAScript    sourceType:  'module',  // Разрешает использование импорта    ecmaFeatures:  {      jsx:  true,  //Разрешает анализ JSX    },    },    rules:  {       // Место для указания правил ESLint. Может использоваться для перезаписи правил, указанных в расширенных конфигах      // например "@typescript-eslint/явный-тип-возврата-функции": "выкл.",    },    settings:  {      react:  {        version:  'detect',  // Указывает eslint-plugin-react автоматически определять версию React для использования      },    },  };     

Расширения и настройки кода VS

Следующий шаг по улучшению – автоматическое исправление и предварительная настройка кода при сохранении.

Установите ESLint и Prettier для VS Code. Это позволит ESLint легко интегрироваться с вашим редактором.

Затем обновите настройки, добавив следующий код в свой .vscode/settings.json:

.vscode/settings.json

         {      "editor.formatOnSave": true  }     

Хуки

Хуки, вроде useState, получают параметр и правильно возвращают состояние и функцию для его установки.

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

         const [user, setUser] = React.useState<IUser>(user);     

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

         const [user, setUser] = React.useState<IUser | null>(null);  // later...  setUser(newUser);     

Обработка событий формы

Один из наиболее распространенных случаев – это правильный ввод using в поле ввода onChange. Вот пример:

         import React from 'react'  const MyInput = () => {    const [value, setValue] = React.useState('')    // The event type is a "ChangeEvent"    // We pass in "HTMLInputElement" to the input    function onChange(e: React.ChangeEvent<HTMLInputElement>) {      setValue(e.target.value)    }    return <input value={value} onChange={onChange} id="input-example"/>  }     

Расширение свойств компонентов

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

         import React from 'react';  type ButtonProps = {      /** the background color of the button */      color: string;      /** the text to show inside the button */      text: string;  }  type ContainerProps = ButtonProps & {      /** the height of the container (value used with 'px') */      height: number;  }  const Container: React.FC<ContainerProps> = ({ color, height, width, text }) => {    return <div style={{ backgroundColor: color, height: `${height}px` }}>{text}</div>  }     

Если вы использовали interface, то можно применить extends, чтобы расширить его, но придется внести пару изменений:

         import React from 'react';  interface ButtonProps {      /** the background color of the button */      color: string;      /** the text to show inside the button */      text: string;  }  interface ContainerProps extends ButtonProps {      /** the height of the container (value used with 'px') */      height: number;  }  const Container: React.FC<ContainerProps> = ({ color, height, width, text }) => {    return <div style={{ backgroundColor: color, height: `${height}px` }}>{text}</div>  }     

Сторонние библиотеки

Мы часто включаем сторонние библиотеки в проекты React и TypeScript. В таких случаях стоит посмотреть, есть ли пакет @types с определениями типов:

         #yarn  yarn add @types/<package-name>  #npm  npm install @types/<package-name>     

Пространство имен @types зарезервировано для определений типов пакетов. Они живут в репозитории под названием DefinitherTyped, который частично поддерживается командой TypeScript, а частично – сообществом.

Итоги

Совместное использование React и TypeScript требует некоторого обучения из-за объема информации, но в долгосрочной перспективе затраты времени окупаются. В своей публикации мы опирались на лучшие практики из статей Джо Превайта, Фернандо Дольо и Мартина Хохеля. Мы постарались осветить самые полезные приемы и моменты, которые помогут использовать TypeScript как часть инструментальной цепочки React. Больше информации вы найдете в официальном справочнике к TypeScript или в публикациях «Библиотеки программиста».

  • 2 views
  • 0 Comment

Leave a Reply

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

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close