Error in vue3 with typescript: unable to assign a ComputeRef<number[]> argument to an iterable<number> in d3.js

This code snippet was originally sourced from an example at https://medium.com/@lambrospd/5-simple-rules-to-data-visualization-with-vue-js-and-d3-js-f6b2bd6a1d40

I attempted to adapt the example to a TypeScript/Vue 3 version, and below is my implementation:

<script lang="ts">
import { ref, defineComponent, computed, toRef } from "vue";
import * as d3 from 'd3';

export default defineComponent({
  name: "StandardMetric",
   props: {
   data: {
      required: true,
      type: Array,
    },
    width: {
      default: 500,
      type: Number,
    },
    height: {
      height: 270,
      type: Number,
    }

   },
   setup: function (props, {attrs, slots, emit}){

      const padding = ref(60);

      const rangeY = computed(() => {
        const defaultHeight = toRef(props, 'height').value;
        const height: Number = defaultHeight?defaultHeight:270 - padding.value;
        const result = [0, height];
        return result;
      });
      
      const rangeX = computed(() => {
        const defaultWidth: Number = toRef(props, 'width').value;
        const width: Number = defaultWidth?defaultWidth:500 - padding.value;
        return [0, width];
      });

      
      const path = computed(()=> {
      // const x = d3.scaleLinear().range(rangeX);
      // v1 error
      // const x = d3.scaleLinear().range(rangeX.value);
      // v2 error

      const x = d3.scaleLinear().range([0, 500]);
      const y = d3.scaleLinear().range(rangeY.value);
      d3.axisLeft().scale(x);
      d3.axisTop().scale(y);
      x.domain(d3.extent(this.data, (d, i) => i));
      y.domain([0, d3.max(this.data, d => d)]);
      return d3.line()
        .x((d, i) => x(i))
        .y(d => y(d));
     }),

When using the computed function in 'path', I encountered an error in the first statement. If I use the v1 statement, the error message says:

argument of type ComputeRef<number[]> is not assignable to iterable

Alternatively, if I use the v2 statement, the error becomes:

argument of type number[] is not assignable to iterable

However, hardcoding it to [0,500] allows it to pass without any errors. I am puzzled about the correct way to write this statement. Can someone offer some guidance or assistance? Thank you.

Answer №1

I haven't had the chance to test it yet, but I suspect the issue may lie in your use of Number

Take a look here for more information on TypeScript

The type names String, Number, and Boolean (capitalized) are valid, but they refer to special built-in types that are seldom used in code. It's best practice to use string, number, or boolean for types instead.

Using uppercase variants in props definition is acceptable - this is necessary for Vue and not related to TypeScript.

Also, both rangeY and rangeX do not need explicit type annotations as TypeScript can infer the types automatically.

UPDATE

My suspicion was correct. Some additional changes were needed to satisfy TypeScript requirements. Below is the updated component (Vue 3.0.11, d3 6.6.2, '@types/d3': 6.3.0, typescript: 4.1.5)

<template>
  <svg
    class="line-chart"
    :viewBox="`0 0 ${width} ${height}`"
  >
    <g >
      <path
        class="line-chart__line"
        :d="path"
      />
    </g>
  </svg>
</template>

<script lang="ts">
import { ref, defineComponent, computed, PropType } from "vue";
import * as d3 from 'd3';

export default defineComponent({
  name: "LineChart",
  props: {
    data: {
      required: true,
      type: Array as PropType<number[]>,
    },
    width: {
      type: Number,
      default: 500,      
    },
    height: {
      type: Number,
      default: 270,      
    }
  },
  setup: function (props){

      const padding = ref(20);

      const rangeY = computed(() => {
        const defaultHeight = props.height;
        const height = defaultHeight ? defaultHeight : 270 - padding.value;        
        return [0, height];
      });
      
      const rangeX = computed(() => {
        const defaultWidth = props.width;
        const width = defaultWidth ? defaultWidth : 500 - padding.value;
        return [0, width];
      });
      
      const path = computed(()=> {
        const x = d3.scaleLinear().domain([0, props.data.length]).range(rangeX.value);
        const y = d3.scaleLinear().domain([0, d3.max(props.data) as number]).range(rangeY.value);
        const axisX = d3.axisLeft(y);
        const axisY = d3.axisBottom(x);
        const line = d3.line()
          .x((d) => x(d[1]))
          .y(d => y(d[0]));
        
        return line(props.data.map((value, index) => [value, index]));
      })

    return {
      path
    }      
  }
});
</script>

<style lang="sass">
.line-chart
  margin: 25px
  &__line
    fill: none
    stroke: #76BF8A
    stroke-width: 3px
</style>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is it possible to set up TypeScript npm packages to be installed in their original TypeScript format rather than JavaScript for the purpose of examining the source code?

Despite my lack of expertise in the inner workings of how a TypeScript library compiles itself to JavaScript before being placed in the node_modules directory, I have a question: Coming from a PHP background, I am accustomed to being able to explore any l ...

Can you test a Vue 3 Composition library that utilizes inject without using a container component?

I am currently referring to a tutorial on developing an authentication composition library by following this guide: Essentially, you create a file /src/components/auth/index.ts where you define refs and "use" functions that are then directly exported. Her ...

What categories do input events fall into within Vue?

What Typescript types should be used for input events in Vue to avoid missing target value, key, or files properties when using Event? For example: <input @input="(e: MISSING_TYPE) => {}" /> <input @keypress="(e: MISSING_TYPE) = ...

webpack is having trouble locating the src file, even though it should not be searching for it in the first place

I'm currently delving into the world of using TypeScript with React and am following a helpful tutorial at: https://blog.logrocket.com/how-why-a-guide-to-using-typescript-with-react-fffb76c61614 However, when attempting to run the webpack command thr ...

Altering the properties of a specified element within TestBed using the overrideComponent method

We are implementing TestBed.overrideComponent() to substitute a component with custom behavior. TestBed.overrideComponent(CoolComponent, { set: { template: '<div id="fake-component">i am the fake component</div>', sel ...

What steps should I take to enable users of my library to customize component templates as needed?

I created a customized component to enhance the appearance of bootstrap form controls, intended for use in various projects. I am considering transforming this into a library (a separate npm package with its own @NgModule), but some projects may wish to mo ...

Encountering a Lint No Nested Ternary Error while utilizing the ternary operator

Is there a way to prevent the occurrence of the "no nested ternary" error in TypeScript? disablePortal options={ // eslint-disable-next-line no-nested-ternary units=== "mm&quo ...

Sinon fails to mock the provided URL when GET request includes parameters

I am currently working on creating test cases for the services in my Angular application and encountering some challenges. Below is the code snippet for the service: /** * Sends http request to fetch client states and territories available for a specifi ...

Setting up Cypress.config file for SQL database testing with Cypress

Currently, I am looking to experiment with SQL databases. I have SqlWorkbench installed and have mysql added in my package file. However, I encountered an issue while attempting to run Cypress as SyntaxError: Unexpected token 'export' The probl ...

Troubles with input handling in Angular

I've been diving into Traversy Media's Angular crash course recently. However, I've hit a roadblock that I just can't seem to get past. The problem arises when trying to style the button using a specific method. Every time I save and pa ...

Mastering asynchronous props handling with Vue 3's composition API

Starting Component: const { receiveData, deletePost, erasePhonebook, fetchCount, issue } = useSections(); const section = ref({}); receiveData(section_id).then((s) => { section.value = s; }); Sub Component: const { section } = defineProps({ secti ...

Setting up Vue CLI 4 with ESLint, TypeScript, Stylelint for SCSS, and Airbnb rules in the VS Code editor with automatic fixes on save

After struggling with configuring Vue CLI 4 with ESLint, Prettier, Airbnb rules, TypeScript, and Vetur, I found myself at a crossroads. The challenges continued to mount as the nature of the problem evolved from my previous attempts.: How to configure Vue ...

What is the process of creating a typeorm relationship between orders and products?

My Orders Entity file in TypeOrm looks like this: @Entity('orders') export class OrdersEntity { @PrimaryGeneratedColumn('uuid') id: string; @CreateDateColumn() created: Date; @UpdateDateColumn() updated: Date; @Column('t ...

Importing a JSON or JSONC file into a vite/typescript project can be easily done

I am looking for a way to seamlessly share my routes between my actix-web backend and Vue with Vue-Router frontend without needing separate route files. I want to define the routes on the frontend without having to make any changes on the server side. If t ...

Using Angular 7 shared service to allow sibling components to exchange data between each other

In my Angular 7 application, I have two sibling components - a configurator component and a custom stepper component. The configurator component is responsible for fetching data from the API and performing calculations on it. I would like to display the ca ...

The initial character of the input must always be a letter

I need assistance with an input element that requires 5 characters, with the first character being a letter: <input mdInput #acronyme placeholder="Company" type="text" maxlength="5" minlength="5" required [value]="acronyme.value.toUpperCase()"> Th ...

Utilizing material-ui with Autocomplete featuring various value and option types

In my code, I am looking to store only an option's ID in a value For autocomplete functionality, the value's type and the option's type need to be the same My solution was to change the value in onChange, which worked successfully However ...

Creating web components with lit-element, leveraging rollup, postcss, and the tailwind framework for packaging

I have been attempting to package a functional web component that was developed using the lit-element/lit-html with the tailwind framework utilizing the postcss plugin from the rollup packager. Upon conducting a rollup, I discovered the compiled js and ht ...

Adjust cursor location in a provided OnTypeFormattingEdits with Monaco Editor

I've implemented the following code to automatically close an XML tag when typing the '>' of an opening tag. Everything is working smoothly so far, however, I am trying to position the cursor between the tags instead of at the end of the ...

Tips for showcasing a string value across various lines within a paragraph using the <br> tag for line breaks

I'm struggling to show a string in a paragraph that includes tags to create new lines. Unfortunately, all I see is the br tags instead of the desired line breaks. Here is my TypeScript method. viewEmailMessage(messageId: number): void { c ...