使用d3.js和TypeScript绘制饼图时的编译错误

Mat*_*son 6 javascript svg d3.js typescript

我试图使用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();
        });
    }

  }
}
Run Code Online (Sandbox Code Playgroud)

代码不会与错误一起编译:

 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'.   
Run Code Online (Sandbox Code Playgroud)

当我尝试将d属性添加到path我的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();
        });
Run Code Online (Sandbox Code Playgroud)

根据文档,弧是"既是对象又是函数".我看到我可以通过调用来访问它,例如arc(datum[, index])通过硬编码arc[0].当我这样做时,我的编译错误消失但svg d中每个path元素的属性都丢失了,我最终得到了一个像svg:

    <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>
Run Code Online (Sandbox Code Playgroud)

我已经将代码作为纯JavaScript运行而没有任何问题.

Mar*_*cka 15

尝试更换

.attr('d', arc)   
Run Code Online (Sandbox Code Playgroud)

.attr('d', <any>arc)  
Run Code Online (Sandbox Code Playgroud)

这隐藏了我的计算机上的编译器错误,但如果这实际上工作......好吧,我不知道.

我对这个问题的理解是你提供了.data带有number值的函数,而TypeScript编译器期望它.attr也包含一个数字,但你提供了一个数字arc.


Mik*_*rer 9

来到这里是因为我在使用 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)
;


Run Code Online (Sandbox Code Playgroud)

旁注:我正在使用 WebStorm,WS 无法找到(自动导入)PieArcDatum - 我必须手动导入它...


mor*_*tic 5

虽然使用<any>将起到避免编译错误的作用,但它首先破坏了进行类型检查的目的.多试错和质量时间之后d3.d.ts定义弧布局,我想出如何使类型排队:

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);
}
Run Code Online (Sandbox Code Playgroud)

除了原始问题,我还展示了如何在饼图中添加标签并保持Typescript的强类型.请注意,此实现利用了允许您为节指定类型的备用构造函数d3.svg.arc<T>(): Arc<T>arc.

更新

上面的代码假定data传递的是一个数字数组.如果您查看代码(特别是访问器n => n.countd => 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);
}
Run Code Online (Sandbox Code Playgroud)

在第二个版本中,不再有任何隐式any类型.另外需要注意的是接口的名称Datum是任意的.您可以将该接口命名为您想要的任何名称,但只需要小心地将所有引用更改Datum为您选择的任何更合适的名称.