Compilation errors when drawing a piechart using d3.js and TypeScript

后端 未结 3 1074
暖寄归人
暖寄归人 2021-02-14 21:23

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 {

            


        
相关标签:
3条回答
  • 2021-02-14 21:37

    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 the d3.d.ts definition for the Arc layout, I figured out how to make the types line up:

    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);
    }
    

    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 the arc 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 accessors n => n.count and d => d.data.label), it is clearly not. Those accessors also have an implicit any type, i.e. (n: any) => n.count. This could possibly throw a runtime error if the n happens to be an object without a count property. Here's a rewrite that makes the shape of the data more explicit:

    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);
    }
    

    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 to Datum to whatever more appropriate name you chose.

    0 讨论(0)
  • 2021-02-14 21:51

    Try to replace

    .attr('d', arc)   
    

    with

    .attr('d', <any>arc)  
    

    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 with number values and TypeScript compiler expects that .attr will contain also a number but you provide an arc instead.

    0 讨论(0)
  • 2021-02-14 21:57

    Came here because I had the same problem with D3 v5!

    Solution: Use the PieArcDatum interface (Very strange name in my opinion!!!)

    Details:

    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)
    ;
    
    
    

    Sidenote: I'm using WebStorm, WS was not able to find (autoimport) PieArcDatum - I had to import it manually...

    0 讨论(0)
提交回复
热议问题