TypeScript 環境での Chart.js の利用について

ブラウザで折れ線グラフとかを表示したいな〜と思ったときは大抵Chart.jsという超有名ライブラリを使うことになる。Chart.jsの使い方は実に簡単で、グラフの設定とデータを投げるだけで大変見目麗しいグラフを表示してくれる。

最近はプラグインを自作するに至ったんだけど、それ以前にもちょいちょいつまづきがあったので忘備録としてメモっておく。

Chart.jsをTypeScript環境で使う

素のChart.jsをReactないしVueで表示するのはそれほど難しくなく、Vanilla JSと同じでCanvas要素をChartクラスに渡すだけで良い。

以下のサンドボックスはReactの例:

Chart.jsをモジュール利用する場合は、必ず importした各機能を register で登録しなければならない。

importできる全機能は説明に書いてある通りなのだけれども、TypeScript環境下だとChart型とChartクラスが喧嘩するので名前を変更しておく。

import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  LineController,
} from "chart.js";

ChartJS.register(
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
);

このregisterによる登録はプラグインを含めた全ての機能に対してやらなきゃいけないので、動作しなかった場合はまずここを確認すること。

useRefの型指定

import type { Chart } from 'chart.js'
// ~~~~
const chartRef = React.useRef<Chart<'line'>>(null)

Optionsの型エラー

設定で、正しく値を使ったはずなのに怒られが発生する場合は

plugins: {
    legend: {
      // The types of 'plugins.legend.position' are incompatible between these types.
      // Type 'string' is not assignable to type '"top" | "right" | "bottom" | "left" | "center" | "chartArea" | _DeepPartialObject<{ [scaleId: string]: number; }> | undefined'
      position: 'top',  
    },
}

Const AssertionsType Assertions を利用する。

// in options
plugins: {
    legend: {
      position: 'top' as const,
    },
}

または

import type {  LayoutPosition } from 'chart.js'

// in options
plugins: {
    legend: {
      position: 'top' as LayoutPosition,
    },
}

全部型調べてアサーションするのは面倒なのでconstでいいと思われ🤔

react-chartjs-2の利用

ReactでChart.jsを利用するのに便利なライブラリが react-chartjs-2 である。

これは公式のサンドボックスなのだが、

インスタンス作るあたりを全部肩代わりしてくれるので大変便利。

プラグインの利用

上記でも書いた通り、プラグインも利用するものはすべてimportしてregisterが必須である。

import {
  Title,
  Tooltip,
  Legend
} from "chart.js";

ChartJS.register(
  Title,
  Tooltip,
  Legend
);

バージョン2まではpluginsオプションでインラインに設定することもできたが、3で使えなくなっている。

Decimation Plugin

巨大なデータをいい感じに間引いてグラフを見やすくするDecimationプラグインというのがある。

これは使用条件が決まっていて、

  1. データセットには「x」のindexAxisが必要
    つまり {x: number, y: number } といった配列にしろってこと
  2. データセットはLineに限る
    折れ線グラフ以外は未対応
  3. データセットのX軸は「linear」または「time」タイプにする
  4. データ解析を使用すると動かないので、 parsing: false にする
  5. データセットオブジェクトは変更可能であること
    プラグインは元のデータをdataset._dataとして保存し、データセットに新しいデータプロパティを定義する
  6. データ件数が多い

上記6点がすべて満たされている場合はプラグインが働いて設定したように間引きをしてくれる。

必要なデータ件数はResponsiveが有効だと画面サイズに依存するので、画面が小さいかつ件数が少ない場合はDecimationされない。

一見プラグインが動いてなく見えるので勘違いしやすいと思う🤔

Scaleの設定

datasetにはラベルを渡さない代わりに、scaleを設定しなければならない。

X軸がlinearの場合:

      scales: {
          x: {
            type: "linear"
          }
        }

X軸がtimeの場合:

      scales: {
          x: {
            type: "time"
          }
        }

外部チャートタイプの利用

公式でサポートされてない、ユーザーが作成したチャートタイプを利用する場合もプラグインとほとんど手順は同じ。

以下は chartjs-chart-boxplot を利用する例

型の拡張

例えばオプションに独自の値を追加したいとかなった時、その追加したいオプションの型を追加しないと怒られが発生する。
Chart.jsはAPIのところでインターフェースが公開されているので、それを利用して拡張する。

設定の追加

  interface ChartConfiguration<TType extends ChartType> {
    myOption: {
      foo: boolean
    }
  }
{
  type: 'boxplot',
   myOption: {
      foo: true,
   },
   data: {},
   options: {}
}

プラグイン設定の追加

declare module 'chart.js' {
  interface PluginOptionsByType<TType extends ChartType> {
    myOption?: MyOptionType
  }
}
plugins: {
    myOption: {}
}

メソッドの追加

declare module 'chart.js' {
  interface Chart<
    TType extends keyof ChartTypeRegistry = keyof ChartTypeRegistry,
    TData = DistributiveArray<ChartTypeRegistry[TType]['defaultDataPoint']>,
    TLabel = unknown
  > {
    myFunction(): void
  }
}
chartRef?.current?.myFunction()

Interaction Modeの追加

declare module 'chart.js' {
  interface InteractionModeMap {
    myInteraction: InteractionModeFunction
  }
}
tooltip: {
  mode: 'myInteraction' as const
},

データセットプロパティの追加

declare module 'chart.js' {
  interface ChartDatasetProperties<TType extends ChartType, TData> {
    myProperty?: boolean | undefined
  }
}
{
  label: "Dataset 1",
  data: [],
  myProperty: false,
},

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください