Angular Project中类型[数字,数字]上不存在D3属性X和属性Y [英] D3 property X and Property Y doesn't exist on type [number, number] in Angular Project

查看:54
本文介绍了Angular Project中类型[数字,数字]上不存在D3属性X和属性Y的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

https://jsfiddle.net/goanhefy/

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

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

问题是我在有角度的CLI中看到错误

src/app/shared/line-graph/line-graph.component.ts(84,40)中的

ERROR: 错误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() {

  }

}

这是我的应用程序组件

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

解决方案

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

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),因此上述两种方法都返回实现此接口的对象,其中一种具有您声明的类型为此,另一个与元组.

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

按以下方式调用方法时:.x( (d) => xScale(d.x)). Typescript将对此进行查看,并且知道您的d变量应该是应该传递给方法xDatum类型,但是您没有告诉它该类型是什么,因此它将抱怨说它不知道对象d上的属性x.既然您没有告诉它期望什么类型,它就假定确实具有类型(又称为[number, number]元组类型)的方法的重载.

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

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

解决方案2 :尽管更合适的解决方法是利用Typescript帮助您.为图形将显示的对象类型创建一个接口.在您的实例中,您似乎正在传递具有xy属性的对象,因此:

interface IGraphDatum {
  x: number;
  y: number;
}

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

代码继续工作的原因是您的对象实际上具有xy属性,因此,已编译的JavaScript执行时没有错误,但是编译器抛出了错误,如您未给出的那样它提供了足够的信息来强制这两个属性都位于传递的对象上.

I have a simple line graph that I've made in D3

https://jsfiddle.net/goanhefy/

I'm trying to integrate this chart into an Angular project and have the data managed by the app component. I've actually recreated it in Angular and posted it online

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

The problem is that I'm seeing an error in the angular CLI

ERROR in src/app/shared/line-graph/line-graph.component.ts(84,40): error TS2339: Property 'x' does not exist on type '[number, number]'. src/app/shared/line-graph/line-graph.component.ts(85,40): error TS2339: Property 'y' does not exist on type '[number, number]'.

While my project still runs, I'd like to figure out what's causing this issue.

Here is my full line graph typescript

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() {

  }

}

And here is my app component

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

解决方案

If you look at the typings file for d3 and the line function it provides, you will see that it has two overloads: export function line(): Line<[number, number]>; and export function line<Datum>(): Line<Datum>;.

The Line<Datum> interface in turn declares three overloads for the x method: x(): (d: Datum, index: number, data: Datum[]) => number;, x(x: number): this; and x(x: (d: Datum, index: number, data: Datum[]) => number): this;. While the naming makes this a little confusing, Datum is really just a generic type(often represented as T in other languages) and therefore the above two methods both return objects that implement this interface, one with a type that you declare for it and the other with a Tuple.

The types referenced are in the index.d.ts file in the @types/d3-shape directory.

When you are calling your method as follows: .x( (d) => xScale(d.x)). Typescript will look at that and knows that your d variable is supposed to be the Datum type that is supposed to be passed into the method x, but you haven't told it what that type is, so it will complain that it doesn't know about a property x on your object d. Since you haven't told it what type to expect, it assumes the overload for the method that does have a type(a.k.a. the [number, number] Tuple type).

There are two ways that you can get the transpilation time error message to go away:

Solution 1: The quick fix would be to update your method call to this: .x( (d: any) => xScale(d.x)). You are then telling Typescript that the d object you are passing is of type any and therefor it won't complain regardless of the property you try to access on it.

Solution 2: The more proper fix though would be to utilize Typescript to help you out. Create an interface for the type of objects that the graph will display. In your instance, it looks like you are passing objects with an x and a y property so:

interface IGraphDatum {
  x: number;
  y: number;
}

Then update your method call to .x( (d: IGraphDatum) => xScale(d.x)).

The reason that the code continues to work is that your object actually has the x and y properties on it, so the transpiled JavaScript executes without error, but the transpiler is throwing the error as you haven't given it enough information to enforce that both of those properties will be on the passed object.

这篇关于Angular Project中类型[数字,数字]上不存在D3属性X和属性Y的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆