Использование Typescript, чтобы ступить на яму реакции-редукции

TypeScript React.js Redux

задний план

Недавно я просматривал исходный код antd, а также сам изучаю синтаксис и использование машинописного текста, а затем я собираюсь привлечь своих младших школьников и учителей для создания музыкального проигрывателя в качестве практики машинописного текста. наступить на яму;

Тогда я не ожидал, что попаду в большую яму в самом начале проекта — в процессе построения проекта react-redux, следующий контент — запись того, как я выбрался из этой глубокой ямы.

Начинать? не могу даже начать

Он попал в яму уже после создания проекта. . .

Создать проект

Процесс создания проекта очень прост, вы можете обратиться к этой статье, которую я написал ранеестатья

Начните карабкаться по яме с mapStateToProps&mapDispatchToProps

Я был не очень знаком с машинописным текстом в начале, поэтому мой первый редьюсер, действие, константа, файлы все заканчивались на .js, только код компонента заканчивался на .tsx, а затем я начал следить за процессом обработки состояния в хранилище mapStateToProps и действия mapDispatchToProps, кто бы мог подумать, что это будет начало кошмара, сначала я написал такой код:

  import { bindActionCreators } from 'redux';
  function mapStateToProps(state: {}) {
    return {
      player: state.PlayerStore,
    };
  }

  function mapDispatchToProps(dispatch: Function) {
    return {
      playerActions: bindActionCreators(actions, dispatch)
    };
  }

然后就开始报错了。 . .

1
1

После долгих метаний я изменил код на этот и смог пройти тест

  import { bindActionCreators, Dispatch } from 'redux';
  function mapStateToProps(state: { PlayerStore: object }) {
    return {
      player: state.PlayerStore,
    };
  }

  function mapDispatchToProps(dispatch: Dispatch<{}>) {
    return {
      playerActions: bindActionCreators<{}>(actions, dispatch)
    };
  }

Тип утверждения компонента реакции восхождения на яму

  interface PlayerPropsClass {
    player: PlayerStateTypes;
    playerActions: actions.PlayerActionsTypes;
  }
  class Player extends React.Component<PlayerPropsClass, {}> {
    constructor(props: object) {
      super(props as PlayerPropsClass);
      this.state = {};
    }

    addOne = (num: number) => this.props.playerActions.count(num);

    subtractOne = (num: number) => this.props.playerActions.subtract(num);

    render() {
      const { countNumber } = this.props.player.toJS();
      return (
        <div className="player">
          <span>{countNumber}</span>
          <button onClick={() => this.addOne(countNumber as number)}>点击+1</button>
          <button onClick={() => this.subtractOne(countNumber as number)}>点击-1</button>
        </div>
      );
    }
  }

  export default connect(mapStateToProps, mapDispatchToProps)(Player as any);

Вот сообщение об ошибке (Player as any)

  [tslint] Type declaration of 'any' loses type-safety. Consider replacing it with a more precise type, the empty type ('{}'), or suppress this occurrence. (no-any)

Затем я изменился на

  export default connect(mapStateToProps, mapDispatchToProps)(Player as {});

все еще ошибка

  [ts]
  类型“{}”的参数不能赋给类型“ComponentType<{ player: object; } & { playerActions: {}; }>”的参数。
  不能将类型“{}”分配给类型“StatelessComponent<{ player: object; } & { playerActions: {}; }>”。
  类型“{}”提供的内容与签名“(props: { player: object; } & { playerActions: {}; } & { children?: ReactNode; }, context?: any): ReactElement<any> | null”不匹配。

Ничего себе, мой разум взрывается, я не знаю, как написать это утверждение. . . Тут я вспомнил пример от antd, который писалReact.ReactElement, а затем случайно нашел в Интернете, что в Node_Modules есть папка @type в Node_Modules, которые находятся на типоре соответствующего типа в пакете узла, поэтому я следовал по этому путиnode_modules/@types/react/index.d.ts,Наконец-то нашелReact.ComponentType<T>, в этом файле много типов, если хотите узнать больше, можете посмотреть сами

  export default connect(mapStateToProps, mapDispatchToProps)(Player as React.ComponentType<PlayerPropsClass>);

Восхождение на яму и редуктор

С вышеперечисленным опытом, плюсздесьПочитав немного кода, я решил изменить действия и редукторы на файлы, оканчивающиеся на ts;

  • src/reducers/index.ts

      import { combineReducers, createStore } from 'redux';
      import PlayerStore from '../containers/Player/reducer';
    
      // 这个是用来使用到mapStateToProps函数的地方给state进行类型检测的
      export interface AppStoreType {
        PlayerStore: object;
      }
    
      const rootReducer = combineReducers({
        PlayerStore,
      });
    
      export default () => {
        return createStore(rootReducer);
      };
  • containers/Demo/reducer.ts

      import * as PlayerTypes from './constant';
      import { fromJS } from 'immutable';
      // 由于使用到immutable,state上面就会使用到immutable的函数,所以需要将其也做成类型检测
      import { ImmutableFuncType } from '../../constant';
    
      interface ActionType {
        type: string;
        countNumber?: number;
      }
    
      interface StoreType {
        countNumber: number;
      }
      // 把当前容器的state组,然后抛出提供使用
      export type PlayerStateTypes = StoreType & ImmutableFuncType;
    
      const PlayerStore: PlayerStateTypes = fromJS({
        countNumber: 0,
      });
    
      export default (state = PlayerStore, action: ActionType) => {
        switch (action.type) {
          case PlayerTypes.COUNT:
            return state.update('countNumber', () => fromJS(action.countNumber));
          default:
            return state;
        }
      };
  • containers/Demo/action.ts

      import * as PlayerTypes from './constant';
    
      export const count = (num: number) => ({
        type: PlayerTypes.COUNT,
        countNumber: num + 1,
      });
    
      export const subtract = (num: number) => ({
        type: PlayerTypes.COUNT,
        countNumber: num - 1,
      });
      // 抛出actions函数的类型以供mapDispatchToProps使用
      export interface PlayerActionsTypes {
        count: Function;
        subtract: Function;
      }
  • containers/Demo/index.tsx

      import * as React from 'react';
      import './style.css';
      import { connect } from 'react-redux';
      import { bindActionCreators, Dispatch } from 'redux';
      import * as actions from './action';
      import { PlayerStateTypes } from './reducer';
      import { AppStoreType } from '../../reducers';
    
      interface PlayerPropsClass {
        player: PlayerStateTypes;
        playerActions: actions.PlayerActionsTypes;
      }
    
      function mapStateToProps(state: AppStoreType) {
        return {
          player: state.PlayerStore,
        };
      }
    
      function mapDispatchToProps(dispatch: Dispatch<{}>) {
        return {
          playerActions: bindActionCreators<{}>(actions, dispatch)
        };
      }
    
      class Player extends React.Component<PlayerPropsClass, {}> {
        constructor(props: object) {
          super(props as PlayerPropsClass);
          this.state = {};
        }
    
        addOne = (num: number) => this.props.playerActions.count(num);
    
        subtractOne = (num: number) => this.props.playerActions.subtract(num);
    
        render() {
          const { countNumber } = this.props.player.toJS();
          return (
            <div className="player">
              <span>{countNumber}</span>
              <button onClick={() => this.addOne(countNumber as number)}>点击+1</button>
              <button onClick={() => this.subtractOne(countNumber as number)}>点击-1</button>
            </div>
          );
        }
      }
    
      export default connect(mapStateToProps, mapDispatchToProps)(Player as React.ComponentType<PlayerPropsClass>);

конец

Благодаря этому небольшому упражнению я действительно ощутил преимущества машинописного текста, то есть об ошибках сообщается во время компиляции, и можно узнать причину ошибки, нет необходимости искать точку ошибки после завершения компиляции, что действительно экономит много времени, особенно он очень удобен для обработки неопределенных и нулевых значений в коде JavaScript. . В общем, писать так легко. Ахахахаха~~~