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

Добавление TypeScript в существующий проект create-react-app

Если вы хотите добавить TypeScript в существующее приложение, установите TypeScript и другие необходимые типы.

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

Затем нам нужно переименовать файлы в .ts или .tsx, а затем запустить сервер. Это автоматически создаст файл tsconfig.json.

Использование TypeScript с действиями

export interface IStartFetchPostsAction extends Action<’StartFetchPosts’> {}
export interface IFetchPostsSuccessAction extends Action<’FetchPostsSuccess’> {
 posts: IPost[];
}
export type PostActions =
 | IStartFetchPostsAction
 | IFetchPostsSuccessAction

Эти типы действий расширяют общий тип Action, поставляемый с библиотекой Redux. Это гарантирует, что мы правильно установили свойство type при использовании действий в нашем коде.

Как видно из вышеизложенного, у нас есть PostsActions тип объединения, который ссылается на 2 действия, указанные выше. Позже мы будем использовать это в редукторе сообщений, чтобы убедиться, что мы работаем с правильными действиями.

Вы можете спросить, откуда мы получаем IPost []. Ну, есть отдельный файл, в котором мы создали интерфейс для сообщений, которые выглядят так.

export interface IPost {
 id: number;
 title: string;
 description: string;
...
}

Поскольку получение сообщений из серверной части должно быть асинхронным действием, вот где Redux-Thunk решает нашу проблему. Однако использование TypeScript здесь будет немного сложнее.

export const fetchPostsActionCreator: ActionCreator<
 ThunkAction<
 Promise<IFetchPostsSuccessAction>, 
 >
> = () => {
 return async (dispatch: Dispatch) => {
 const startFetchPostsAction: IStartFetchPostsAction = {
 type: ‘StartFetchPosts’,
 };
 dispatch(startFetchPostsAction);
 const posts = await fetch("API URL");
 const fetchPostsSuccessAction: IFetchPostsSuccessAction = {
 posts,
 type: ‘FetchPostsSuccess’,
 };
 return dispatch(fetchPostsSuccessAction);
 };
};

ActionCreator - это общий тип из библиотеки Redux, который принимает тип, возвращаемый создателем действия. Создатель действия выше возвращает функцию, которая вернет IFetchPostsSuccessAction. ThunkAction впервые может показаться вам странным. Но это общий, который поставляется с библиотекой Redux-Thunk.

Использование TypeScript с редукторами

Давайте посмотрим, как будет выглядеть наш Reducer, когда мы реализуем TypeScript:

import { Reducer } from "redux";
import {
  IPostsState,
  PostsActions,
} from "./PostsTypes";
const initialPostState: IPostsState = {
  posts: [],
  isLoading: false
};
const postsReducer: Reducer<IPostsState, PostsActions> = (
 state = initialPostsState,
 action,
) => {
 switch (action.type) {
 case ‘StartFetchPosts’: {
 return {
 …state,
 isloading: true,
 };
 }
 case ‘FetchPostsSuccess’: {
 return {
 …state,
 posts: action.posts,
 isloading: false,
 };
 }
 }
return state;
};

Также нам понадобится root reducer

import { combineReducers } from 'redux';
import PostsReducer from './reducers/PostsReducer'
const rootReducer = combineReducers<IAppState>({
 Posts: postsReducer
});
export default rootReducer;

Мы используем общий тип Reducer из библиотеки Redux, передавая наш тип состояния с типом объединения PostsActions.

Аргумент action в каждом случае оператора switch имеет суженный тип до определенного действия, которое имеет отношение к конкретному случаю.

Использование TypeScript в магазине

Мы используем общий тип Store из библиотеки Redux, передавая тип состояния нашего приложения, который в этом проекте равен IAppState. Давайте внесем изменения в наш store.js

export function configureStore(): Store<IAppState> {
 const store = createStore(rootReducer, undefined, applyMiddleware(thunk));
 return store;
}

Использование TypeScript внутри компонента

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

import * as React from "react";
import { connect } from "react-redux";
import { RouteComponentProps } from "react-router-dom";
import { fetchPosts } from "./PostsActions";
import { IPost } from "./PostsData";
import PostsList from "./PostsList";
import { IAppState } from "./Store";
interface IProps extends RouteComponentProps {
  loading: boolean;
  posts: IPost[];
}
class PostsPage extends React.Component<IProps> {
  public componentDidMount() {
    this.props.fetchPosts();
  }
public render() {
  ...
    return (
      <div className="page-container">
 
        <PostsList
          posts={this.props.posts}
          loading={this.props.loading}
        />
      </div>
    );
  }
}
const mapStateToProps = (store: IAppState) => {
  return {
    loading: store.posts.isLoading,
    posts: store.posts.posts
  };
};
const mapDispatchToProps = (dispatch: any) => {
  return {
    fetchPosts: () => dispatch(fetchPosts())
  };
};
export default connect(
  mapStateToProps,
  mapDispatchToProps
)(PostsPage);

Функция mapStateToProps использует наш тип IAppState, поэтому ссылки на состояние магазинов строго типизированы.

Подведение итогов

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