Type issues with d3js

huangapple go评论70阅读模式
英文:

Type issues with d3js

问题

我遇到了以下错误,当尝试使用d3构建图表时。这是一个基本的垂直条形图,所有数据都来自服务器端。JS代码执行Ajax调用,然后将数据发送到d3。

TS2345: 参数类型'(this: BaseType | SVGRectElement, d: Analytics) => number | undefined'无法分配给类型'string | number | boolean | readonly (string | number)[] | ValueFn<BaseType | SVGRectElement, Analytics, string | number | boolean | readonly (string | number)[] | null> | null'
  类型'(this: BaseType | SVGRectElement, d: Analytics) => number | undefined'无法分配给类型'ValueFn<BaseType | SVGRectElement, Analytics, string | number | boolean | readonly (string | number)[] | null>'
    类型'number | undefined'无法分配给类型'string | number | boolean | readonly (string | number)[] | null'
      类型'undefined'无法分配给类型'string | number | boolean | readonly (string | number)[] | null'
    78 |       .attr("class", "bar")
    79 |       // .attr("x", (d) => x_scaler(d, x_scale))
  > 80 |       .attr("x", (d) => x_scale(d.period_ended_at))
       |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    81 |       .attr("y", (d) => y_scale(d.price_cents))
    82 |       .attr("width", x_scale.bandwidth())
    83 |       .attr("height", (d) => height - y_scale(d.price_cents));

以下是完整的代码:

type Analytics = {
  id: number;
  period_ended_at: string;
  price_cents: number;
  fee_cents: number;
};

const data: Analytics[] = await get_data() // 这将正常返回一个被转换为`Analytics`的JSON。我已在控制台中检查了这一点,它有效。

const width = 600
const height = 400

const x_scale = d3.scaleBand().range([0, width]).padding(0.1);
const y_scale = d3.scaleLinear().range([height, 0]);

const svg = d3.select(".chart")
  .append("svg")
  .attr("width", width)
  .attr("height", height)
  .style("border", "1px solid black")

x_scale.domain(data.map((d: Analytics) => d.period_ended_at));
y_scale.domain([0, d3.max(data, (d: any) => d.price_cents)]);

svg
  .selectAll("rect")
  .data(data)
  .join("rect")
  .attr("class", "bar")
  .attr("x", (d) => x_scale(d.period_ended_at))
  .attr("y", (d) => y_scale(d.price_cents))
  .attr("width", x_scale.bandwidth())
  .attr("height", (d) => height - y_scale(d.price_cents));

非常感谢您的帮助!

英文:

I'm getting the following error, when trying to build a graph with d3. It's a basic vertical bar chart with all data provided from the server side. The JS code is doing an Ajax call and then sending the data to d3.

TS2345: Argument of type &#39;(this: BaseType | SVGRectElement, d: Analytics) =&gt; number | undefined&#39; is not assignable to parameter of type &#39;string | number | boolean | readonly (string | number)[] | ValueFn&lt;BaseType | SVGRectElement, Analytics, string | number | boolean | readonly (string | number)[] | null&gt; | null&#39;.
  Type &#39;(this: BaseType | SVGRectElement, d: Analytics) =&gt; number | undefined&#39; is not assignable to type &#39;ValueFn&lt;BaseType | SVGRectElement, Analytics, string | number | boolean | readonly (string | number)[] | null&gt;&#39;.
    Type &#39;number | undefined&#39; is not assignable to type &#39;string | number | boolean | readonly (string | number)[] | null&#39;.
      Type &#39;undefined&#39; is not assignable to type &#39;string | number | boolean | readonly (string | number)[] | null&#39;.
    78 |       .attr(&quot;class&quot;, &quot;bar&quot;)
    79 |       // .attr(&quot;x&quot;, (d) =&gt; x_scaler(d, x_scale))
  &gt; 80 |       .attr(&quot;x&quot;, (d) =&gt; x_scale(d.period_ended_at))
       |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    81 |       .attr(&quot;y&quot;, (d) =&gt; y_scale(d.price_cents))
    82 |       .attr(&quot;width&quot;, x_scale.bandwidth())
    83 |       .attr(&quot;height&quot;, (d) =&gt; height - y_scale(d.price_cents));

Here is the full code:

type Analytics = {
  id: number;
  period_ended_at: string;
  price_cents: number;
  fee_cents: number;
};

const data: Analytics[] = await get_data() // This is properly return a JSON that is being casted to `Analytics`. I&#39;ve checked this in the console and this is working.

const width = 600
const height = 400

const x_scale = d3.scaleBand().range([0, width]).padding(0.1);
const y_scale = d3.scaleLinear().range([height, 0]);

const svg = d3.select(&quot;.chart&quot;)
  .append(&quot;svg&quot;)
  .attr(&quot;width&quot;, width)
  .attr(&quot;height&quot;, height)
  .style(&quot;border&quot;, &quot;1px solid black&quot;)

x_scale.domain(data.map((d: Analytics) =&gt; d.period_ended_at));
y_scale.domain([0, d3.max(data, (d: any) =&gt; d.price_cents)]);

svg
  .selectAll(&quot;rect&quot;)
  .data(data)
  .join(&quot;rect&quot;)
  .attr(&quot;class&quot;, &quot;bar&quot;)
  .attr(&quot;x&quot;, (d) =&gt; x_scale(d.period_ended_at))
  .attr(&quot;y&quot;, (d) =&gt; y_scale(d.price_cents))
  .attr(&quot;width&quot;, x_scale.bandwidth())
  .attr(&quot;height&quot;, (d) =&gt; height - y_scale(d.price_cents));

Any help would be greatly appreciated. Thanks!

答案1

得分: 1

错误详细说明了x_scale()的返回类型是number | undefined。因为attr("x", value)期望的是number而不是undefined,类型检查器报告了这个错误。当你在tsconfig.json中启用strictNullChecks时会出现这种情况。

你可以通过在要覆盖类型的表达式后添加!来强制类型检查器假设该值始终不会为null:

.attr("x", (d) => x_scale(d.period_ended_at)!)
//                                  ^ 这表明该表达式不是undefined

从技术上讲,这会从联合类型number | undefined中移除undefined,从而满足attr("x", value)调用所需的类型。

请注意,这不会改变代码本身的行为,它只是告诉类型检查器信任作者而不是自己的类型推断。如果函数返回undefined,你的代码仍然会出现问题,因为在编译成JS时,!后缀操作符会被移除。

你可以在这里了解更多信息:

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#non-null-assertion-operator-postfix-

英文:

The error very verbosely explains that the return type of x_scale() is number | undefined. Because attr(&quot;x&quot;, value) expects number not undefined, the type checker reports that as an error. This happens when you enable strictNullChecks in your tsconfig.json.

You can force the type checker to assume the value will always be non-nullish by appending ! to the expression whose type you want to override:

.attr(&quot;x&quot;, (d) =&gt; x_scale(d.period_ended_at)!)
//                                          ^ this asserts that the expression
//                                            is not undefined

Technically, this removes undefined from the union type number | undefined which then satisfies the type required for the attr(&quot;x&quot;, value) call.

Note that this does not change the behaviour of the code itself, it just tells the typechecker to trust the author instead of its own type inferrence. If the function were to return undefined, your code would still break since the ! postfix operator is removed when compiling to JS.

You can read more about this here:

https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#non-null-assertion-operator-postfix-

huangapple
  • 本文由 发表于 2023年2月26日 19:53:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/75571786.html