如何检查联合类型参数类型并实现Chart.js数据转换?
问题描述
我们定义了如下图表数据类型:
type 1DChartPoint = { x: number } type 2DChartPoint = 1DChartPoint & { y: number } type 3DChartPoint = 2DChartPoint & { z: number } type DataSet<T extends 1DChartPoint> = { data: number[] | T[] } type BarChartDataSet = DataSet<1DChartPoint> type ScatterplotDataSet = DataSet<2DChartPoint> type BubbleplotDataSet = DataSet<3DChartPoint>
需要实现一个转换方法,将任意DataSet转换为Chart.js的ChartData对象,当前的转换代码存在类型检查逻辑错误:
private convert<T extends 1DChartPoint>(data: DataSet<T>) { let convertedData; if (data is number[]) { convertedData = data } else if (data is 1DChartPoint) { convertedData = data.map(val => val.x); } const result: chartjs.ChartData = { datasets: [convertedData] } return result; }
请问如何正确判断data.data是number[]还是1DChartPoint[]类型,并完成正确的转换?
解决方案
核心问题修正
原代码的错误在于:
- 混淆了
DataSet类型本身和其内部的data属性,参数data是DataSet对象,而非直接的数组/点对象 - TypeScript中不能直接用
data is number[]这类语法做类型判断,需要自定义类型守卫函数来实现准确的类型检查
实现步骤与代码
- 定义类型守卫函数,用于判断数组类型
- 提取
DataSet中的data属性进行检查 - 根据类型执行对应转换逻辑
完整代码:
// 类型守卫:判断是否为number数组 function isNumberArray(arr: unknown): arr is number[] { return Array.isArray(arr) && arr.every(item => typeof item === 'number'); } // 类型守卫:判断是否为1DChartPoint数组 function is1DChartPointArray<T extends 1DChartPoint>(arr: unknown): arr is T[] { return Array.isArray(arr) && arr.every(item => typeof item === 'object' && item !== null && 'x' in item && typeof item.x === 'number' ); } private convert<T extends 1DChartPoint>(dataSet: DataSet<T>): chartjs.ChartData { let convertedData: number[]; const targetData = dataSet.data; if (isNumberArray(targetData)) { convertedData = targetData; } else if (is1DChartPointArray(targetData)) { convertedData = targetData.map(point => point.x); } else { // 处理非法数据,可根据业务需求抛出错误或返回空数组 convertedData = []; } // Chart.js的ChartData中datasets每个元素需是包含data属性的对象 return { datasets: [{ data: convertedData }] }; }
关键说明
- 类型守卫函数:通过
is关键字定义返回类型,让TypeScript在类型检查时能识别判断后的类型,避免类型报错 - 数组判断逻辑:先用
Array.isArray确认是数组,再用every校验每个元素的类型/结构,确保整个数组符合目标类型 - ChartData结构修正:原代码直接将
convertedData放入datasets数组不符合Chart.js规范,需要包装为{ data: convertedData }的数据集对象
内容的提问来源于stack exchange,提问作者Benjin
相关产品推荐
相关产品推荐

