I am attempting to draw a pie chart using the d3.js library and TypeScript. I have the following code:
"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();
});
}
}
}
The code does not compile with the error:
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'.
The compilation error is showing up when I try to add the d
attribute to each of the path
elements on my svg:
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();
});
According to the documentation an arc is "is both an object and a function." and I see that I can access it by calling arc(datum[, index])
, by just hardcoding arc[0]
for example. When I do this my compilation error goes away but the d
attribute of each path
element in the svg is missing and I end up with an svg like:
<svg height="360" width="360">
<g transform="translate(180,180)">
<path fill="0.35327279710072423"></path>
<path fill="0.6333000506884181"></path>
<path fill="0.9358429045830001"></path>
</g>
</svg>
I've had the code running as pure JavaScript with no problems.
Try to replace
with
This hides the compiler error on my computer but if that actually work... Well, I don't know.
My understanding of the problem is that you provide
.data
function withnumber
values and TypeScript compiler expects that.attr
will contain also a number but you provide anarc
instead.Came here because I had the same problem with D3 v5!
Solution: Use the
PieArcDatum
interface (Very strange name in my opinion!!!)Details:
Sidenote: I'm using WebStorm, WS was not able to find (autoimport) PieArcDatum - I had to import it manually...
While using
<any>
will work to avoid compilation errors, it defeats the purpose of having type checking in the first place. After much trial and error and quality time with thed3.d.ts
definition for the Arc layout, I figured out how to make the types line up:In addition to the original question, I've also shown how to add labels to a pie chart and maintain the strong typing of Typescript. Note that this implementation takes advantage of the alternate constructor
d3.svg.arc<T>(): Arc<T>
that allows you to specify a type for thearc
sections.Update
The above code assumes that the
data
being passed is an array of numbers. If you look at the code though (in particular the accessorsn => n.count
andd => d.data.label
), it is clearly not. Those accessors also have an implicitany
type, i.e.(n: any) => n.count
. This could possibly throw a runtime error if then
happens to be an object without acount
property. Here's a rewrite that makes the shape of thedata
more explicit:In this second version, there are no longer any implicit
any
types. Another thing to note is that the name of the interface,Datum
, is arbitrary. You could name that interface anything you wanted to, but would just have to be careful to change all references toDatum
to whatever more appropriate name you chose.