Angular 项目中的类型 [number, number] 上不存在 D3 属性 X 和属性 Y

onT*_*net 3 d3.js angular

我有一个在 D3 中制作的简单折线图

https://jsfiddle.net/goanhefy/

我正在尝试将此图表集成到 Angular 项目中,并由应用程序组件管理数据。我实际上已经在 Angular 中重新创建了它并将其发布到网上

https://stackblitz.com/edit/angular-irjxus?file=app%2FShared%2Fline-graph%2Fline-graph.component.css

问题是我在 Angular CLI 中看到错误

src/app/shared/line-graph/line-graph.component.ts(84,40) 中的错误:错误 TS2339:类型“[number,number]”上不存在属性“x”。src/app/shared/line-graph/line-graph.component.ts(85,40):错误 TS2339:类型“[number,number]”上不存在属性“y”。

在此输入图像描述 虽然我的项目仍在运行,但我想找出导致此问题的原因。

这是我的全线图打字稿

import { Component, OnInit, OnChanges, ViewChild, ElementRef, Input, ViewEncapsulation } from '@angular/core';
import * as d3 from 'd3';

@Component({
  selector: 'app-line-graph',
  templateUrl: './line-graph.component.html',
  styleUrls: ['./line-graph.component.css'],
  encapsulation: ViewEncapsulation.None
})
export class LineGraphComponent implements OnInit, OnChanges {
  @ViewChild('line') private lineContainer: ElementRef;
  @Input() private lineData: Array<any>;
  private margin: any = 25;
  private chart: any;
  private width = 500;
  private height = 500;
  private xScale: any;
  private yScale: any;
  private colors: any;
  private xAxis: any;
  private yAxis: any;
  private axisLength: any = this.width - 2 * this.margin;


  constructor() { }

  ngOnInit() {
    console.log(this.lineData);
    this.createChart();
    if (this.lineData) {
      this.updateChart();
    }
  }

  ngOnChanges() {
    if (this.chart) {
      this.updateChart();
    }
  }

  createChart() {
    /*
    const data: Array<any> = [
      {x: 0, y: 4},
      {x: 1, y: 9},
      {x: 2, y: 6},
      {x: 4, y: 5},
      {x: 6, y: 7},
      {x: 7, y: 3},
      {x: 9, y: 2}
  ];*/

    const element = this.lineContainer.nativeElement;
    const svg = d3.select(element).append('svg')
                .attr('width', this.width)
                .attr('height', this.height)
                .style('border', '1px solid');

    // Create Scales
    const xScale = d3.scaleLinear()
                   .domain([0, 10])
                   .range([0, this.axisLength]);

    const yScale = d3.scaleLinear()
                   .domain([0, 10])
                   .range([0, this.axisLength]);

    // Create axis
    this.xAxis = d3.axisBottom(xScale);
    this.yAxis = d3.axisLeft(yScale);

    svg.append('g')
       .classed('x-axis', true)
       .attr('transform', () => 'translate(' + this.margin + ',' + (this.height - this.margin) + ')')
       .call(this.xAxis);

    svg.append('g')
       .classed('y-axis', true)
       .attr('transform', () => 'translate(' + this.margin + ',' + this.margin + ')')
       .call(this.yAxis);


    const line = d3.line()
                   .x( (d) => xScale(d.x))
                   .y( (d) => yScale(d.y));

    svg.append('path')
       .attr('d', line(this.lineData))
       .attr('fill', 'none')
       .attr('stroke', 'red')
       .attr('transform', () => 'translate(' + this.margin + ',' + this.margin + ')');
  }


  updateChart() {

  }

}
Run Code Online (Sandbox Code Playgroud)

这是我的应用程序组件

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  private chartData: Array<any>;
  private lineData: Array<object>;

  ngOnInit() {

    this.lineData = [
      {x: 0, y: 4},
      {x: 1, y: 9},
      {x: 2, y: 6},
      {x: 4, y: 5},
      {x: 6, y: 7},
      {x: 7, y: 3},
      {x: 9, y: 2}
    ];
    console.log(this.lineData, 'Hello from app component');

    // give everything a chance to load before starting animation
    setTimeout(() => {
      this.generateData();

      // change data periodically
      setInterval(() => this.generateData(), 10000);
    }, 1000);
  }


  generateData() {
    this.chartData = [];
    for (let i = 0; i < (8 + Math.floor(Math.random() * 10)); i++) {
      this.chartData.push([
        `Index ${i}`,
        Math.floor(Math.random() * 100)
      ]);
    }
     //console.log(this.chartData);
  }
}
Run Code Online (Sandbox Code Playgroud)

pei*_*ent 7

如果您查看 d3 的类型文件及其line提供的函数,您将看到它有两个重载:export function line(): Line<[number, number]>;export function line<Datum>(): Line<Datum>;

Line<Datum>接口又为该x方法声明了三个重载:x(): (d: Datum, index: number, data: Datum[]) => number;x(x: number): this;x(x: (d: Datum, index: number, data: Datum[]) => number): this;。虽然命名使这有点令人困惑,Datum但实际上只是一种泛型类型(通常以T其他语言表示),因此上述两种方法都返回实现此接口的对象,一个具有您为其声明的类型,另一个具有您声明的类型一个元组。

引用的类型位于目录index.d.ts中的文件中@types/d3-shape

当您按如下方式调用方法时:.x( (d) => xScale(d.x)). Typescript 会查看它并知道你的d变量应该是Datum应该传递到方法中的类型x,但是你没有告诉它该类型是什么,所以它会抱怨它不知道x你的对象的属性d。由于您没有告诉它期望什么类型,它假定具有类型(也称为元组类型)的方法的重载[number, number]

有两种方法可以消除转译时间错误消息:

解决方案 1:快速修复方法是将您的方法调用更新为:.x( (d: any) => xScale(d.x))。然后,您告诉 Typescript 您传递的 d 对象是 any 类型,因此无论您尝试访问它的属性是什么,它都不会抱怨。

解决方案 2:更正确的解决方案是利用 Typescript 来帮助您。为图表将显示的对象类型创建一个界面。在您的实例中,看起来您正在传递带有xy属性的对象,因此:

interface IGraphDatum {
  x: number;
  y: number;
}
Run Code Online (Sandbox Code Playgroud)

然后将您的方法调用更新为.x( (d: IGraphDatum) => xScale(d.x)).

代码继续工作的原因是你的对象实际上具有 和x属性y,因此转译后的 JavaScript 执行时没有错误,但转译器会抛出错误,因为你没有给它足够的信息来强制执行这两个操作属性将位于传递的对象上。