【问题标题】:Compilation errors when drawing a piechart using d3.js and TypeScript使用 d3.js 和 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 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>

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

【问题讨论】:

    标签: javascript d3.js svg typescript


    【解决方案1】:

    尝试替换

    .attr('d', arc)   
    

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

    这隐藏了我计算机上的编译器错误,但如果它真的有效......好吧,我不知道。

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

    【讨论】:

    • 那行得通。这是我第一次看到&lt;any&gt; 语法,需要进一步调查
    • 你可以用谷歌搜索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】:

    虽然使用&lt;any&gt; 可以避免编译错误,但它首先违背了进行类型检查的目的。经过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&lt;T&gt;(): Arc&lt;T&gt;,它允许您为arc 部分指定类型。

    更新

    上面的代码假定被传递的data 是一个数字数组。如果您查看代码(特别是访问器n =&gt; n.countd =&gt; d.data.label),显然不是。这些访问器还有一个隐含的any 类型,即(n: any) =&gt; 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.arc>()。它有效,但它在做什么?是否将 arc 转换为输入 >,一个数字?
    • 是的,或多或少。 svg.Arc 类可以从大多数任何类型的数据中创建弧线。内尖括号内的部分指定pie.Arc 中表示的数据的形状。如果您的数据只是一个数字数组,那么&lt;number&gt; 将起作用。如果它更复杂,那么您将需要定义一个类似Datum 的类来表示数据的形状并使用它。
    猜你喜欢
    • 2016-05-26
    • 2017-11-12
    • 2017-10-27
    • 1970-01-01
    • 2016-12-08
    • 1970-01-01
    • 2016-10-02
    • 2020-12-09
    • 1970-01-01
    相关资源
    最近更新 更多