使用 d3.js 和 TypeScript 绘制饼图时出现编译错误
Posted
技术标签:
【中文标题】使用 d3.js 和 TypeScript 绘制饼图时出现编译错误【英文标题】:Compilation errors when drawing a piechart using d3.js and TypeScript 【发布时间】:2016-01-11 12:44:34 【问题描述】:我正在尝试使用 d3.js 库和 TypeScript 绘制饼图。我有以下代码:
"use strict";
module Chart
export class chart
private chart: d3.Selection<string>;
private width: number;
private height: number;
private radius: number;
private donutWidth: number;
private dataset: label: string, count: number [];
private color: d3.scale.Ordinal<string, string>;
constructor(container: any)
this.width = 360;
this.height = 360;
this.radius = Math.min(this.width, this.height) / 2;
this.donutWidth = 75;
this.dataset = [
label: 'Road', count: 5500 ,
label: 'Bridge', count: 8800 ,
label: 'Tunnel', count: 225 ,
];
this.color = d3.scale.category10();
this.init(container);
private init(container)
this.chart = d3.select(container).append('svg')
.attr('width', this.width)
.attr('height', this.height)
.append('g')
.attr('transform', 'translate(' + (this.width / 2) +
',' + (this.height / 2) + ')');
draw()
var arc = d3.svg.arc()
.innerRadius(this.radius - this.donutWidth) // NEW
.outerRadius(this.radius);
var pie = d3.layout.pie()
.sort(null);
var path = this.chart.selectAll('path')
.data(pie(this.dataset.map(function(n)
return n.count;
)))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d, i)
return Math.random();
);
代码无法编译并出现错误:
Argument of type 'Arc<Arc>' is not assignable to parameter of type '(datum: Arc<number>, index: number, outerIndex: number) => string | number | boolean'.
>> Types of parameters 'd' and 'datum' are incompatible.
>> Type 'Arc' is not assignable to type 'Arc<number>'.
>> Property 'value' is missing in type 'Arc'.
当我尝试将d
属性添加到我的svg 上的每个path
元素时出现编译错误:
var path = this.chart.selectAll('path')
.data(pie(this.dataset.map(function(n)
return n.count;
)))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d, i)
return Math.random();
);
根据文档,弧是“既是对象又是函数”。我发现我可以通过调用arc(datum[, index])
来访问它,例如只需硬编码arc[0]
。当我这样做时,我的编译错误消失了,但是 svg 中每个 path
元素的 d
属性丢失了,我最终得到了一个像这样的 svg:
<svg >
<g transform="translate(180,180)">
<path fill="0.35327279710072423"></path>
<path fill="0.6333000506884181"></path>
<path fill="0.9358429045830001"></path>
</g>
</svg>
我已经将代码作为纯 javascript 运行,没有任何问题。
【问题讨论】:
【参考方案1】:尝试替换
.attr('d', arc)
与
.attr('d', <any>arc)
这隐藏了我计算机上的编译器错误,但如果它真的有效......好吧,我不知道。
我对这个问题的理解是,您为 .data
函数提供了 number
值,而 TypeScript 编译器期望 .attr
也包含一个数字,但您提供了一个 arc
。
【讨论】:
那行得通。这是我第一次看到<any>
语法,需要进一步调查
你可以用谷歌搜索TypeScript casting
。请注意,它是编译时强制转换。【参考方案2】:
来到这里是因为我在使用 D3 v5 时遇到了同样的问题!
解决方案:使用PieArcDatum
接口(在我看来很奇怪的名字!!!)
详情:
import PieArcDatum from 'd3-shape';
...
type Population = time: string, population: number; ;
...
const svg = element.append("g")
.attr("transform", `translate($300, $160)`)
;
const pie = d3.pie<Population>()
.sort(null)
.value((record) => record.population);
const path = d3.arc<PieArcDatum<Population>>()
.innerRadius(0)
.outerRadius(150)
;
// Beim selectAll kommt eine leere Selection zurück da es noch keinen Circle gibt
const data = pie(worldPopulation);
const arch = svg.selectAll(".arc")
.data(data)
.enter()
.append("g")
.attr("class", "arc")
;
arch.append('path')
.attr("d", path)
;
旁注:我正在使用 WebStorm,WS 无法找到(自动导入)PieArcDatum - 我必须手动导入它...
【讨论】:
@MattWattson:这应该是公认的答案,因为它正确地解决了问题,而不仅仅是抛弃它。【参考方案3】:虽然使用<any>
可以避免编译错误,但它首先违背了进行类型检查的目的。经过the d3.d.ts
definition for the Arc layout 的反复试验和质量时间,我想出了如何使类型对齐:
function draw()
let arc = d3.svg.arc<d3.layout.pie.Arc<number>>()
.innerRadius(this.radius - this.donutWidth)
.outerRadius(this.radius);
let pie = d3.layout.pie().sort(null);
let tfx = (d: d3.layout.pie.Arc<number>): string => `translate($arc.centroid(d))`);
// create a group for the pie chart
let g = this.chart.selectAll('g')
.data(pie(this.dataset.map(n => n.count)))
.enter().append('g');
// add pie sections
g.append('path').attr('d', arc);
// add labels
g.append('text').attr('transform', tfx).text(d => d.data.label);
除了最初的问题,我还展示了如何向饼图添加标签并保持 Typescript 的强类型。请注意,此实现利用了the alternate constructor d3.svg.arc<T>(): Arc<T>
,它允许您为arc
部分指定类型。
更新
上面的代码假定被传递的data
是一个数字数组。如果您查看代码(特别是访问器n => n.count
和d => d.data.label
),显然不是。这些访问器还有一个隐含的any
类型,即(n: any) => n.count
。如果n
恰好是一个没有count
属性的对象,这可能会引发运行时错误。这是一个重写,使data
的形状更加明确:
interface Datum
label: string;
count: number;
function draw()
// specify Datum as shape of data
let arc = d3.svg.arc<d3.layout.pie.Arc<Datum>>()
.innerRadius(this.radius - this.donutWidth)
.outerRadius(this.radius);
// notice accessor receives d of type Datum
let pie = d3.layout.pie<Datum>().sort(null).value((d: Datum):number => d.count);
// note input to all .attr() and .text() functions
// will be of type d3.layout.pie.Arc<Datum>
let tfx = (d: d3.layout.pie.Arc<Datum>): string => `translate($arc.centroid(d))`;
let text = (d: d3.layout.pie.Arc<Datum>): string => d.data.category;
// create a group for the pie chart
let g = this.chart.selectAll('g')
.data(pie(data))
.enter().append('g');
// add pie sections
g.append('path').attr('d', arc);
// add labels
g.append('text').attr('transform', tfx).text(text);
在第二个版本中,不再有任何隐含的any
类型。另外需要注意的是接口的名称Datum
是任意的。您可以为该接口命名任何您想要的名称,但只需小心地将所有对 Datum
的引用更改为您选择的更合适的名称。
【讨论】:
我也有同样的问题。我不明白如何 var arc = d3.svg.arc() beomces = d3.svg.arcsvg.Arc
类可以从大多数任何类型的数据中创建弧线。内尖括号内的部分指定pie.Arc
中表示的数据的形状。如果您的数据只是一个数字数组,那么<number>
将起作用。如果它更复杂,那么您将需要定义一个类似Datum
的类来表示数据的形状并使用它。以上是关于使用 d3.js 和 TypeScript 绘制饼图时出现编译错误的主要内容,如果未能解决你的问题,请参考以下文章