TypeScript + React: путь к идеально типизированному коду26.07.2024 10:45
Привет, Хабр!
Частенько сталкиваются с проблемой поддержания типовой безопасности в React-проекте. Код разрастается, и управление типами становится всё сложнее. Ошибки, вызванные неправильной типизацией, приводят к крашам и длительным отладкам. Тогда приходит время внедрения TypeScript!
В статье рассмотрим как TypeScript может помочь решить проблемы с типизацией и сделать React-код идеально типизированным.
Строгая типизация и Type Inference в TypeScript
Строгий режим TypeScriptstrict — это конфигурация, которая включает ряд некоторых строгих проверок типов.
Чтобы включить строгий режим в проекте, необходимо изменить файл конфигурации TypeScript tsconfig.json:
{
"compilerOptions": {
"strict": true
}
}
Это автоматом включает несколько поднастроек:
noImplicitAny: отключает неявное присвоение типа any. Все переменные должны иметь явный тип.
strictNullChecks: обспечивает строгую проверку null и undefined. Это предотвращает использование переменных, которые могут быть null или undefined, без соответствующей проверки.
strictFunctionTypes: включает строгие проверки типов для функций.
strictPropertyInitialization: проверяет, что все обязательные свойства инициализируются в конструкторе класса.
noImplicitThis: отлючает неявное присвоение типа any для this в функциях.
alwaysStrict: включает строгий режим JavaScript во всех файлах.
Пример строгого режима:
function add(a: number, b: number): number {
return a + b;
}
let result = add(2, 3); // OK
let result2 = add('2', 3); // ошибка компиляции: тип 'string' не может быть присвоен параметру типа 'number'
Вывод типов (Type Inference) позволяет автоматически определяет типы переменных и выражений на основе их значения или контекста использования.
Когда мы объявляем переменную или функцию без явного указания типа, TypeScript пытается вывести тип автоматом на основе присвоенного значен:
let x = 3; // TypeScript выводит тип 'number'
let y = 'privet'; // TypeScript выводит тип 'string'
let z = { name: 'Artem', age: 30 }; // TypeScript выводит тип { name: string; age: number }
TypeScript автоматически определяет тип переменных x, y и z на основе их значений.
Иногда вывод типов может быть недостаточно точным или полезным, например тут:
let items = ['apple', 'banana', 42]; // Тип выводится как (string | number)[]
Мссив items имеет тип (string | number)[], что может не соответствовать ожидаемому поведению. В таких случаях лучше явно указать тип.
Переходим к следующему пункту — правильной типизации Props и State в React с TypeScript
Правильная типизация Props и State в React с TypeScript
Правильное определение типов для Props и State помогает создать более структурированный код.
В TypeScript есть два основных способа определения типов: интерфейсы и типы. Хотя оба подхода имеют схожие возможности, есть некоторые различия:
Интерфейсы:
Обычно их используют для определения структур данных и контрактов для публичных API.
Поддерживают декларативное слияние.
Лучше подходят для объектов с множеством свойств.
Типы:
Используются для определения алиасов типов, особенно для объединений и пересечений типов.
Более гибкие.
Лучше подходят для простых объектов, состояний и внутренних компонентов.
Union типы позволяют объединять несколько типов, а intersection типы — пересекать их:
type Status = 'success' | 'error' | 'loading';
interface Response {
data: string;
}
type ApiResponse = Response & { status: Status };
Переходим к следующему поинту — пользовательские хуки.
Пользовательские хуки
Пользовательские хуки в React позволяют инкапсулировать и переиспользовать логику состояния и побочных эффектов.
Пользовательский хук — это функция, имя которой начинается с use, и которая может использовать другие хуки внутри себя. С помощью этого можно выносить повторяющуюся логику состояния или побочных эффектов в отдельные функции, которые можно переиспользовать в различных компонентах.
Пример создания простого пользовательского хука для управления состоянием счетчика:
import { useState } from 'react';
/**
* Пользовательский хук useCounter.
* @param initialValue начальное значение счетчика.
* @returns Текущее значение счетчика и функции для его увеличения и сброса.
*/
function useCounter(initialValue: number) {
const [count, setCount] = useState(initialValue);
const increment = () => setCount(count + 1);
const reset = () => setCount(initialValue);
return { count, increment, reset };
}
export default useCounter;
На этом моменте хотелось уже закончить статью, но есть еще один важный поинт — внешние библиотеки.
Интеграция и типизация внешних библиотек
Большинство популярных JS-библиотек имеют типы, которые можно установить через npm или yarn. Эти типы находятся в специальном пространстве имен @types.
Установка типов через npm:
npm install @types/library-name
Установка типов через yarn:
yarn add @types/library-name
Пример установки типов для библиотеки lodash:
npm install lodash @types/lodash
После установки типов можно использовать библиотеку с полной типовой поддержкой. Пример с использованием lodash:
import _ from 'lodash';
const numbers: number[] = [1, 2, 3, 4, 5];
const doubled = _.map(numbers, num => num * 2);
console.log(doubled); // [2, 4, 6, 8, 10]
TypeScript автоматически распознает типы, предоставляемые библиотекой lodash, благодаря установленным типам.
Но как мы знаем не все в этом мире идеально и поэтому — не все библиотеки имеют готовые типы. В таких случаях можно создать собственные декларации типов, чтобы избежать использования типа any.
Предположим, есть библиотека example-library, у которой нет готовых типов. Создадим собственные декларации типов для этой библиотеки.
Создаем файл с типами, например example-library.d.ts.
Определяем типы для используемых функций и объектов библиотеки.
После создания этого файла можно использовать библиотеку с типовой поддержкой:
import { exampleFunction, exampleConstant } from 'example-library';
const result: number = exampleFunction('test');
console.log(result);
console.log(exampleConstant);
Флаг skipLibCheck в файле tsconfig.json позволяет пропускать проверку типов библиотек. Полезно, когда типы библиотек содержат ошибки, но очень хочется продолжить компиляцию проекта.
{
"compilerOptions": {
"skipLibCheck": true
}
}
Финальные слова
TypeScript в React-проектах — это не просто рекомендация, а необходимость для тех, кто хочет создать надежное, масштабируемое, а самое главное — легкое в сопровождении приложение.
Больше про языки программирования эксперты OTUS рассказывают в рамках практических онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке.