React и TypeScript — две замечательные технологии, используемые сегодня многими разработчиками. Знание того, как
Давайте погрузимся!
Как React и TypeScript работают вместе
Прежде чем мы начнем, давайте еще раз посмотрим, как React и TypeScript работают вместе. React — это «библиотека JavaScript для создания пользовательских интерфейсов», а TypeScript — «типизированный надмножество JavaScript, который компилируется в простой JavaScript». Используя их вместе, мы, по сути, создаем наши пользовательские интерфейсы, используя типизированную версию JavaScript.
Причина, по которой вы можете использовать их вместе, заключается в том, чтобы получить преимущества языка со статической типизацией (TypeScript) для вашего пользовательского интерфейса. Это означает большую безопасность и меньшее количество ошибок, доставляемых во внешний интерфейс.
Компилирует ли TypeScript мой код React?
Распространенный вопрос, на который всегда полезно ответить, — компилирует ли TypeScript ваш код React. Принцип работы TypeScript аналогичен этому взаимодействию:
ТС: «Эй, это весь код твоего пользовательского интерфейса?»
Реакция: «Ага!»
ТС: «Круто! Я собираюсь скомпилировать его и убедиться, что вы ничего не пропустили».
Реакция: «Звучит хорошо для меня!»
Так что ответ — да, это так! Но позже, когда мы рассмотрим tsconfig.jsonнастройки, большую часть времени вы захотите использовать файлы «noEmit»: true. Это означает, что TypeScript не будет выдавать JavaScript после компиляции. Это потому, что обычно мы используем TypeScript только для проверки типов.
Вывод обрабатывается в настройках CRA с помощью
Напомним, что TypeScript компилирует ваш код React для проверки кода. Он не выдает никакого вывода JavaScript (в большинстве сценариев). Вывод
Может ли TypeScript работать с React и webpack?
Да, TypeScript может работать с React и webpack. К счастью для вас, в документации по
Надеюсь, это даст вам небольшое освежение в том, как они работают вместе. Теперь о лучших практиках!
Лучшие практики
Мы изучили наиболее распространенные вопросы и составили этот удобный список наиболее распространенных вариантов использования React с TypeScript. Таким образом, вы можете использовать эту статью в качестве справочника в своих собственных проектах.
Конфигурация
Одна из наименее увлекательных, но наиболее важных частей разработки — конфигурация. Как мы можем настроить вещи в кратчайшие сроки, чтобы обеспечить максимальную эффективность и производительность? Мы обсудим настройку проекта, включая:
tsconfig.json
ESLint
красивее
Расширения и настройки VS Code.
Настройка проекта
Самый быстрый способ запустить приложение React/TypeScript — использовать
npx
Это даст вам необходимый минимум, чтобы начать писать React с помощью TypeScript. Несколько заметных отличий:
расширение.tsxфайла
вtsconfig.json
Это tsxдля «TypeScript JSX». Это tsconfig.jsonфайл конфигурации TypeScript, в котором установлены некоторые значения по умолчанию. Ссылается
tsconfig.json
К счастью для нас, для нас генерируется последний шаблон React/TypeScript tsconfig.json. Тем не менее, они добавляют необходимый минимум для начала работы. Мы предлагаем вам изменить свой, чтобы он соответствовал приведенному ниже. Мы также добавили комментарии, объясняющие назначение каждой опции:
{
«compilerOptions»: {
«target»: «es5», // Specify ECMAScript target version
«lib»: [
«dom»,
«dom.iterable»,
«esnext»
], // List of library files to be included in the compilation
«allowJs»: true, // Allow JavaScript files to be compiled
«skipLibCheck»: true, // Skip type checking of all declaration files
«esModuleInterop»: true, // Disables namespace imports (import * as fs from «fs») and enables CJS/AMD/UMD style imports (import fs from «fs»)
«allowSyntheticDefaultImports»: true, // Allow default imports from modules with no default export
«strict»: true, // Enable all strict type checking options
«forceConsistentCasingInFileNames»: true, // Disallow
«module»: «esnext», // Specify module code generation
«moduleResolution»: «node», // Resolve modules using Node.js style
«isolatedModules»: true, // Unconditionally emit imports for unresolved files
«resolveJsonModule»: true, // Include modules imported with.json extension
«noEmit»: true, // Do not emit output (meaning do not compile code, only perform type checking)
«jsx»: «react», // Support JSX in.tsx files
«sourceMap»: true, // Generate corrresponding.map file
«declaration»: true, // Generate corresponding.d.ts file
«noUnusedLocals»: true, // Report errors on unused locals
«noUnusedParameters»: true, // Report errors on unused parameters
«incremental»: true, // Enable incremental compilation by reading/writing information from prior compilations to a file on disk
«noFallthroughCasesInSwitch»: true // Report errors for fallthrough cases in switch statement
},
«include»: [
«src/**/*» // *** The files TypeScript should type check ***
],
«exclude»: [«node_modules», «build»] // *** The files to not type check ***
}
Дополнительные рекомендации исходят от сообщества
ESLint/красивее
Чтобы убедиться, что ваш код соответствует правилам проекта или вашей команды, а стиль согласован, рекомендуется настроить ESLint и Prettier. Чтобы заставить их играть хорошо, выполните следующие действия, чтобы настроить его.
Установите необходимые зависимости для разработчиков:
yarn add eslint @
Создайте.eslintrc.jsфайл в корне и добавьте следующее:
module.exports = {
parser: '@
extends: [
‘plugin: react/recommended’, // Uses the recommended rules from @
‘plugin: @
],
parserOptions: {
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
sourceType: ‘module’, // Allows for the use of imports
ecmaFeatures: {
jsx: true, // Allows for the parsing of JSX
},
},
rules: {
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
// e.g. «@
},
settings: {
react: {
version: ‘detect’, // Tells
},
},
};
Добавьте зависимости Prettier:
yarn add prettier
Создайте.prettierrc.jsфайл в корне и добавьте следующее:
module.exports = {
semi: true,
trailingComma: ‘all’,
singleQuote: true,
printWidth: 120,
tabWidth: 4,
};
Обновите.eslintrc.jsфайл:
module.exports = {
parser: '@
extends: [
‘plugin: react/recommended’, // Uses the recommended rules from @
‘plugin: @
+ ‘prettier/@
+ ‘plugin: prettier/recommended’, // Enables
],
parserOptions: {
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
sourceType: ‘module’, // Allows for the use of imports
ecmaFeatures: {
jsx: true, // Allows for the parsing of JSX
},
},
rules: {
// Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
// e.g. «@
},
settings: {
react: {
version: ‘detect’, // Tells
},
},
};
Эти рекомендации взяты из ресурса сообщества, написанного Робертом Купером под названием «Использование ESLint и Prettier в проекте TypeScript «. Если вы посетите этот ресурс, вы сможете узнать больше о том, «почему» стоят за этими правилами и конфигурациями.
Расширения и настройки кода VS
Мы добавили ESLint и Prettier, и следующим шагом по улучшению нашего DX является автоматическое исправление/улучшение нашего кода при сохранении.
Сначала установите расширение ESLint и расширение Prettier для VS Code. Это позволит ESLint легко интегрироваться с вашим редактором.
Затем обновите настройки рабочей области, добавив следующее.vscode/settings.json:
{
«editor.formatOnSave»: true
}
Это позволит VS Code творить чудеса и исправлять ваш код при сохранении. Оно прекрасно!
Эти предложения также взяты из ранее связанной статьи «Использование ESLint и Prettier в проекте TypeScript «Роберта Купера.
Примечание: чтобы узнать больше о React.FC, смотрите здесь и читайте здесь о React.ReactNode.
Компоненты
Одной из основных концепций React являются компоненты. Здесь мы будем ссылаться на стандартные компоненты React v16.8, то есть те, которые используют хуки, а не классы.
В общем, по основным компонентам есть на что обратить внимание. Давайте посмотрим на пример:
import React from ‘react’
// Written as a function declaration
function Heading (): React.ReactNode {
return My Website Heading
}
// Written as a function expression
const OtherHeading: React.FC = () => My Website Heading
Обратите внимание на ключевое отличие здесь. В первом примере мы пишем нашу функцию как объявление функции. Мы аннотируем возвращаемый тип, React.Nodeпотому что это то, что он возвращает. Напротив, во втором примере используется функциональное выражение. Поскольку второй экземпляр возвращает функцию, а не значение или выражение, мы аннотируем тип функции для React.FCReact «Функциональный компонент».
Это может сбивать с толку, чтобы помнить два. В основном это вопрос выбора дизайна. Что бы вы ни выбрали для своего проекта, используйте его последовательно.
Реквизит
Следующее основное понятие, которое мы рассмотрим, — реквизит. Вы можете определить свои реквизиты, используя либо интерфейс, либо тип. Давайте посмотрим на другой пример:
import React from ‘react’
interface Props {
name: string;
color: string;
}
type OtherProps = {
name: string;
color: string;
}
// Notice here we’re using the function declaration with the interface Props
function Heading ({ name, color }: Props): React.ReactNode {
return My Website Heading
}
// Notice here we’re using the function expression with the type OtherProps
const OtherHeading: React.FC
My Website Heading
Когда дело доходит до типов или интерфейсов, мы предлагаем следовать рекомендациям, представленным
«Всегда используйте интерфейс для общедоступного
«рассмотрите возможность использования типа для реквизитов и состояния вашего компонента React, потому что он более ограничен».
Вы можете прочитать больше об обсуждении и увидеть удобную таблицу сравнения типов и интерфейсов здесь.
Давайте посмотрим на еще один пример, чтобы мы могли увидеть
import React from ‘react’
type Props = {
/** color to use for the background */
color?: string;
/** standard children prop: accepts any valid React Node */
children: React.ReactNode;
/** callback function passed to the onClick handler*/
onClick: () => void;
}
const Button: React.FC
return
}
В этом
Всегда добавляйте описательные комментарии к своим свойствам, используя нотацию TSDoc /** comment */.
Независимо от того, используете ли вы типы или интерфейсы для свойств вашего компонента, используйте их последовательно.
Когда props являются необязательными, обрабатывайте их соответствующим образом или используйте значения по умолчанию.
Крючки
К счастью, вывод типа TypeScript хорошо работает при использовании хуков. Это означает, что вам не о чем беспокоиться. Например, возьмем этот пример:
// `value` is inferred as a string
// `setValue` is inferred as (newValue: string) => void
const [value, setValue] = useState ('')
TypeScript выводит значения, данные для использования useStateхуком. Это та область, где React и TypeScript просто работают вместе, и это прекрасно.
В редких случаях, когда вам нужно инициализировать хук с нулевым значением, вы можете использовать дженерик и передать объединение, чтобы правильно ввести свой хук. См. этот пример:
type User = {
email: string;
id: string;
}
// the generic is the < >
// the union is the User | null
// together, TypeScript knows, «Ah, user can be User or null».
const [user, setUser] = useState
Другое место, где TypeScript сияет с помощью хуков, — это userReducer, где вы можете воспользоваться преимуществами размеченных объединений. Вот полезный пример:
type AppState = {};
type Action =
| { type: «SET_ONE»; payload: string }
| { type: «SET_TWO»; payload: number };
export function reducer (state: AppState, action: Action): AppState {
switch (action.type) {
case «SET_ONE»:
return {
...state,
one: action.payload // `payload` is string
};
case «SET_TWO»:
return {
...state,
two: action.payload // `payload` is number
};
default:
return state;
}
}
Источник:
Красота здесь заключается в полезности разрозненных союзов. Обратите внимание Actionна объединение двух похожих друг на друга объектов. Свойство typeявляется строковым литералом. Разница между этим и типом stringзаключается в том, что значение должно соответствовать литеральной строке, определенной в типе. Это означает, что ваша программа более безопасна, поскольку разработчик может вызвать только действие, для которого установлен typeключ «SET_ONE»или «SET_TWO».
Как видите, хуки не усложняют природу проекта React и TypeScript. Во всяком случае, они хорошо подходят для дуэта.
Общие случаи использования
Этот раздел посвящен наиболее распространенным случаям использования, когда люди спотыкаются при использовании TypeScript с React. Мы надеемся, что, поделившись этим, вы избежите ловушек и даже поделитесь этими знаниями с другими.
Обработка событий формы
Одним из наиболее распространенных случаев является правильный ввод 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
setValue (e.target.value)
}
return
}
Расширение свойств компонента
Иногда вы хотите взять реквизиты компонента, объявленные для одного компонента, и расширить их, чтобы использовать их в другом компоненте. Но вы можете изменить один или два. Ну, помните, как мы рассматривали два способа ввода реквизитов компонентов, типов и интерфейсов? В зависимости от того, что вы использовали, определяется, как вы расширяете свойства компонента. Давайте сначала посмотрим на способ использования 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
return <div style={{ backgroundColor: color, height: `${height}px` }}>{text}
}
Если вы объявили свои реквизиты с помощью 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
return <div style={{ backgroundColor: color, height: `${height}px` }}>{text}
}
Оба метода решают проблему. Вам решать, что использовать. Лично мне расширение интерфейса кажется более читабельным, но в конечном счете это зависит от вас и вашей команды.
Вы можете прочитать больше об обеих концепциях в Справочнике по TypeScript:
Типы пересечений
Расширение интерфейсов
Сторонние библиотеки
Будь то для клиента GraphQL, такого как Apollo, или для тестирования с
#yarn
yarn add @types/<
#npm
npm install @types/<
Например, если вы используете Jest, вы можете сделать это, запустив:
#yarn
yarn add @types/jest
#npm
npm install @types/jest
Это даст вам дополнительную безопасность типов всякий раз, когда вы используете Jest в своем проекте.
Пространство @typesимен зарезервировано для определений типов пакетов. Они живут в репозитории под названием DefinitelyTyped, который частично поддерживается командой TypeScript и частично сообществом.
Должны ли они быть сохранены как dependenciesили devDependenciesв моем package.json?
Короткий ответ: «это зависит». В большинстве случаев они могут выйти devDependenciesиз строя, если вы создаете
На Stack Overflow есть несколько ответов на этот вопрос, которые вы можете проверить для получения дополнительной информации.
Что произойдет, если у них нет пакета @types?
Если вы не найдете @typesпакет в npm, у вас есть два варианта:
Добавьте базовый файл объявления
Добавить подробный файл декларации
Первый вариант означает, что вы создаете файл на основе имени пакета и кладете его в корень. Если, например, нам нужны типы для нашего пакета
declare module ‘
Это не обеспечит вам безопасность типов, но разблокирует вас.
Более подробный файл объявления будет там, где вы добавляете типы для библиотеки/пакета:
declare namespace bananaJs {
function getBanana (): string;
function addBanana (n: number) void;
function removeBanana (n: number) void;
}
Если вы никогда не писали файл объявления, то предлагаем вам взглянуть на руководство в официальном TypeScript Handbook.
Резюме
Лучшее совместное использование React и TypeScript требует некоторого обучения