TS怎么重构之前class中注入的方法?

有一个react的历史项目需要重构,都是class组件,多个组件间有些复用方法,通过在componentDidMount中,对this进行utils方法注入,注入之后类的实例就拥有了utils中的方法,但是放到TS的环境,会报错类型上不存在类上不存在注入的属性,应该怎么处理呢?

markPoints

关键代码如下所示:

类组件

import {
  DataMark,
} from '../widgets/utils';

componentDidMount() {
    if (this._echarts) {
      const echartsIns = this._echarts.getEchartsInstance();
      // 初始化标注
      DataMark.init(this, echartsIns);
    }
}

componentDidUpdate(prevProps, prevState, snapshot) {
    //以下markPoints方法是通过utils注入进来的
    //js环境运行没有问题,在TS下会报错,提示没有markPoints属性
    //如何在TS中声明这些属性呢?
    this.markPoints(
      getValue(setting, 'custom.dataMark', []),
      getValue(option, 'series'),
      getValue(setting, 'custom.dataLabel', false)
    );();
  }

utils中DataMark的内容如下:

const fns = {
  markPoints(points: any[], series: any[], showDataLabel: any) {
    if (points.length > 0) {
      series.forEach(
        (
          s: {
            data: any[];
            markPoint: {
              symbol: string;
              symbolSize: number;
              symbolOffset: (string | number)[];
              label: { show: boolean };
              itemStyle: { color: string };
              zlevel: number;
              data: any;
            };
          },
          index: any
        ) => {
          const markPoints = points.filter(
            (mark: {
              seriesIndex: any;
              valueName: any;
              value: any;
              category: any;
            }) =>
              mark.seriesIndex === index &&
              s.data.some(
                (item: { valueName: any; value: any; category: any }) =>
                  item &&
                  item.valueName === mark.valueName &&
                  item.value === mark.value &&
                  item.category === mark.category
              )
          );

          if (markPoints.length > 0) {
            s.markPoint = {
              symbol: DATA_MARK_SYMBOL,
              symbolSize: 16,
              symbolOffset: showDataLabel ? [0, '-200%'] : [0, -10],
              label: {
                show: false,
              },
              itemStyle: {
                color: '#4159F7',
              },
              zlevel: 2,
              data: markPoints.map(
                (markPoint: {
                  id: any;
                  category: { toString: () => any };
                  value: any;
                  content: any;
                }) => ({
                  name: markPoint.id,
                  coord: [markPoint.category.toString(), markPoint.value],
                  category: markPoint.category,
                  value: markPoint.value,
                  content: markPoint.content,
                })
              ),
            };
          }
        }
      );
    }
  },
};

function onSelect(this: any, params) {
  const dataMarkStatus = _.get(this.props, 'widgetDesign.dataMarkStatus');

  if (
    dataMarkStatus === DATA_MARK_STATUS.SELECT &&
    params.componentType !== 'markPoint'
  ) {
    this.props.dispatch({
      type: 'widgetDesign/updateState',
      payload: {
        dataMarkStatus: DATA_MARK_STATUS.ADD,
        dataMarkData: params,
      },
    });
  }
}

function onView(this: any, params) {
  const dataMarkStatus = _.get(this.props, 'widgetDesign.dataMarkStatus');

  if (
    params.componentType === 'markPoint' &&
    dataMarkStatus !== DATA_MARK_STATUS.SELECT
  ) {
    // 设置显示位置
    let style = {};
    if (params.event) {
      const { target } = params.event.event;
      const { top, left } = target.getBoundingClientRect();
      const { offsetX, offsetY } = params.event;

      style = {
        position: 'absolute',
        top: top + offsetY - 50,
        left: left + offsetX - 75,
      };
    }

    const _container = document.createElement('div');
    document.body.appendChild(_container);

    ReactDOM.render(
      <Modal
        visible
        width={150}
        onCancel={() => {
          ReactDOM.unmountComponentAtNode(_container);
          document.body.removeChild(_container);
        }}
        footer={null}
        closable={false}
        style={style}
        maskStyle={{ backgroundColor: 'transparent' }}
        className={styles.dataMarkView}
      >
        {params.data.content}
      </Modal>,
      _container
    );
  }
}

const dataMark = {
  init(
    context: any,
    echartsIns: {
      on: (
        arg0: string,
        arg1: { (params: any): void; (params: any): void }
      ) => void;
    }
  ) {
    const { isEdit } = context.props;

    // 编辑标注
    if (isEdit) {
      echartsIns.on('click', onSelect.bind(context));
    }
    // 查看标注
    echartsIns.on('click', onView.bind(context));

    // 注入方法
    Object.keys(fns).forEach((key) => {
      context[key] = fns[key];
    });
  },
};

export default dataMark;
阅读 1.5k
2 个回答
  1. 不用class,改用hook的写法
  2. 继续用class,util改为高阶函数写法

通过 interface 扩展定义,如下图

image.png

注意:上图调用 a.method() 是会出错的,因为接口虽然声明了,但没有实际注入这个方法。注入的过程你的程序里应该是实现了的。


补充:

src 目录下加个 shims.d.ts 文件,里面大概是这样写,试试看:

import { Component } from "react";

declare module "react" {
    interface Component {
        hello?: string;
    }
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题