Search code examples
reactjstypescriptecharts

React apache echarts typescript error - type '0' can't be used to index type 'Arrayable<LineSeriesOption$1>'


I use an e-chart in react, and I need to get a data length from LineSeriesOption. I am doing it like this:

const dataLen = option.series?.[0].data.length;

But, I get an error:

[1] src/react_components/e-charts/LineChart.tsx(66,25): error TS7053: Element implicitly has an 'any' type because expression of type '0' can't be used to index type 'Arrayable<LineSeriesOption$1>'.

This is the full code of the component:

import type { LineSeriesOption } from "echarts/charts";
import { LineChart } from "echarts/charts";
import type { GridComponentOption, TitleComponentOption, TooltipComponentOption } from "echarts/components";
import {
    DataZoomComponent,
    GridComponent,
    LegendComponent,
    TitleComponent,
    ToolboxComponent,
    TooltipComponent,
} from "echarts/components";
import type { ComposeOption, ECharts, SetOptionOpts } from "echarts/core";
import { getInstanceByDom, init, use } from "echarts/core";
import { CanvasRenderer } from "echarts/renderers";
import { CSSProperties, ReactNode, useEffect, useRef, useState } from "react";

use([
    LegendComponent,
    LineChart,
    GridComponent,
    TooltipComponent,
    TitleComponent,
    ToolboxComponent,
    DataZoomComponent,
    CanvasRenderer,
]);

export type EChartsOption = ComposeOption<
    LineSeriesOption | TitleComponentOption | GridComponentOption | TooltipComponentOption
>;

export interface ReactEChartsProps {
    option: EChartsOption;
    style?: CSSProperties;
    settings?: SetOptionOpts;
}

let currentIndex = -1;

export function EChartLineChart({ option, style }: ReactEChartsProps): ReactNode {
    const chartRef = useRef<HTMLDivElement | null>(null);
    const [chartInitialized, setChartInitialized] = useState(false);

    useEffect(() => {
        let chart: ECharts | undefined;
        if (chartRef.current !== null) {
            chart = init(chartRef.current as HTMLDivElement);
            setChartInitialized(true);
        }

        function resizeChart() {
            chart?.resize();
        }

        window.addEventListener("resize", resizeChart);

        return () => {
            chart?.dispose();
            window.removeEventListener("resize", resizeChart);
        };
    }, []);

    useEffect(() => {
        const canvas = chartRef.current!.querySelector<HTMLCanvasElement>("canvas");
        const chart = getInstanceByDom(chartRef.current as HTMLDivElement);
        const dataLen = option.series?.[0].data.length;

        const handleKeydown = (e: KeyboardEvent) => {
            if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
                chart?.dispatchAction({
                    type: "downplay",
                    seriesIndex: 0,
                    dataIndex: currentIndex,
                });
                currentIndex =
                    e.key === "ArrowRight"
                        ? (currentIndex + 1) % dataLen
                        : currentIndex <= 0
                        ? dataLen - 1
                        : currentIndex - 1;
                chart?.dispatchAction({
                    type: "highlight",
                    seriesIndex: 0,
                    dataIndex: currentIndex,
                });
                chart?.dispatchAction({
                    type: "showTip",
                    seriesIndex: 0,
                    dataIndex: currentIndex,
                });
            }
        };
        const addKeydownListener = () => window.addEventListener("keydown", handleKeydown);
        const removeKeydownListener = () => window.removeEventListener("keydown", handleKeydown);
        canvas?.setAttribute("tabindex", "0");
        canvas?.addEventListener("focusin", addKeydownListener);
        canvas?.addEventListener("focusout", removeKeydownListener);

        return () => {
            window.removeEventListener("keydown", handleKeydown);
            canvas?.removeEventListener("focusin", addKeydownListener);
            canvas?.removeEventListener("focusout", removeKeydownListener);
        };
    }, [chartInitialized]);

    useEffect(() => {
        if (chartRef.current !== null) {
            const chart = getInstanceByDom(chartRef.current as HTMLDivElement);
            chart!.setOption(option);
        }
    }, [option]);

    return <div ref={chartRef} style={{ width: "100%", height: "250px", ...style }} />;
}

How can I fix this typescript error?


Solution

  • According to echarts/types/dist/shared.d.ts

    declare type Arrayable<T> = T | T[];

    For example, Arrayable<number> means either number or number[].

    So the type of option.series is LineSeriesOption[] or LineSeriesOption.

    Therefore, you can use type narrowing to check if an array or not, and then access data length accordingly.

    For example:

    let dataLen = 0;
    if (option.series)
      dataLen = Array.isArray(option.series)
        ? option.series[0].data?.length || 0  // if option.series is an array, use the first element
        : option.series.data?.length || 0;    // if option.series is not an array, use it directly
    

    This way, you can fix the error.

    To learn more, please see ECharts documentation.

    And you can see the whole example here: codesandbox.io