ChartJS自定义squareRoot刻度遇TS类型错误,如何解决?
问题背景
按照ChartJS文档实现自定义平方根刻度,先定义了继承自Scale的SquareRootAxis类:
import { Scale, LinearScale, BubbleDataPoint, Chart, ChartTypeRegistry, ScatterDataPoint} from 'chart.js'; export default class SquareRootAxis extends Scale { static defaults: {} = {}; _startValue: any; _valueRange: number; constructor(cfg: { id: string; type: string; ctx: CanvasRenderingContext2D; chart: Chart<keyof ChartTypeRegistry, (number | ScatterDataPoint | BubbleDataPoint)[], unknown>; }) { super(cfg); this._startValue = undefined; this._valueRange = 0; } parse(raw: any, index: number) { const value = LinearScale.prototype.parse.apply(this, [raw, index]); return isFinite(value) && value > 0 ? value : null; } determineDataLimits() { this.min = 0.5 this.max=31 } buildTicks() { const userDefinedTicks = [1, 2, 3, 5, 7, 10, 15, 20, 25, 30]; const ticks = []; for (const tick of userDefinedTicks) { ticks.push({ value: tick }); } return ticks; } /** * @protected */ configure() { const start = this.min; super.configure(); this._startValue = Math.pow(start, 1/2); this._valueRange = Math.pow(this.max, 1/2) - Math.pow(start, 1/2); } getPixelForValue(value: number | undefined) { if (value === undefined ) { value = this.min; } return this.getPixelForDecimal(value === this.min ? 0 : (Math.pow(value, 1/2) - this._startValue) / this._valueRange); } getValueForPixel(pixel: number) { const decimal = this.getDecimalForPixel(pixel); return Math.pow(this._startValue + decimal * this._valueRange, 2); } }
随后在React-chartjs-2的Scatter图表中导入使用:
import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, } from 'chart.js'; import { Scatter } from 'react-chartjs-2'; import SquareRootAxis from './sqaureRootAxis'; ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, SquareRootAxis ); SquareRootAxis.id = 'squareRoot'; export function Chart(props: { chartData: ChartData }) { return <Scatter data={props.chartData} options={{ responsive: true, scales: { x: { type: 'squareRoot', }, }, }} />; }
设置刻度类型为squareRoot时出现TypeScript错误:
Type '"squareRoot"' is not assignable to type '"time" | "linear" | "logarithmic" | "category" | "timeseries"'.ts(2322)
index.esm.d.ts(3618, 27): The expected type comes from property 'type' which is declared here on type '_DeepPartialObject<{ type: "time"; } & Omit<CartesianScaleOptions...'
问题原因
TypeScript默认不知道你新增的自定义刻度类型,ChartJS官方类型定义仅包含内置刻度类型,因此会触发类型不匹配错误。
解决步骤
1. 扩展ChartJS类型声明
创建一个自定义类型文件(如src/chartjs-custom-scale.d.ts),通过模块扩充将自定义刻度类型加入ChartJS的类型系统:
import { ChartTypeRegistry, CartesianScaleOptions } from 'chart.js'; declare module 'chart.js' { // 注册自定义刻度的配置选项 interface ChartTypeRegistry { squareRoot: { options: CartesianScaleOptions; }; } // 扩展笛卡尔刻度类型,包含自定义刻度 type CartesianScaleType = keyof ChartTypeRegistry; }
2. 修正自定义刻度类的继承与静态ID
自定义刻度应继承自LinearScale而非基础Scale,这样能复用内置线性刻度的核心逻辑,同时将id设为类的静态属性:
import { LinearScale, BubbleDataPoint, Chart, ChartTypeRegistry, ScatterDataPoint} from 'chart.js'; export default class SquareRootAxis extends LinearScale { static id = 'squareRoot'; // 静态声明ID,无需后续赋值 static defaults: {} = {}; _startValue: number; _valueRange: number; constructor(cfg: { id: string; type: string; ctx: CanvasRenderingContext2D; chart: Chart<keyof ChartTypeRegistry, (number | ScatterDataPoint | BubbleDataPoint)[], unknown>; }) { super(cfg); this._startValue = 0; this._valueRange = 0; } // 其余方法保持不变 }
移除使用代码中的SquareRootAxis.id = 'squareRoot';,因为已经通过静态属性定义。
3. 确保TypeScript识别自定义类型文件
在tsconfig.json的include数组中添加自定义类型文件路径:
{ "compilerOptions": { // 你的编译选项 }, "include": [ "src/**/*", "src/chartjs-custom-scale.d.ts" // 替换为你的实际文件路径 ] }
原理说明
ChartJS的TypeScript类型基于声明文件实现,自定义扩展需要通过模块扩充告知TypeScript存在新的刻度类型,这样在配置scales.x.type = 'squareRoot'时,类型检查器会认可该值的合法性。
内容的提问来源于stack exchange,提问作者guy

