apertureless / vue-chartjs

📊 Vue.js wrapper for Chart.js
https://vue-chartjs.org
MIT License
5.54k stars 837 forks source link

TypeError: _super.apply is not a function #598

Closed 4b11b4 closed 4 years ago

4b11b4 commented 4 years ago

Expected Behavior

No errors to console.

Actual Behavior

Error is thrown on chart creation.

Environment

import { Scatter } from "vue-chartjs";
import { Component, Prop, Watch } from "vue-property-decorator";

export interface IPlot {
    data: Object,
    options: Object,
}

@Component({
    "extends": Scatter,
})

export default class BasePlot extends Scatter {
    @Prop({ required: true })
    plot!: IPlot; // `!` means we know this variable will not be null

    isMounted = false;

    mounted() {
        // Overwriting base render method with actual data.
        this.isMounted = true;
        this.renderChart(this.plot.data, this.plot.options);
    }

    @Watch("plot", { deep: false, immediate: false })
    onPlotChanged(value: Object, oldValue: Object) {
        if (this.isMounted) { //catch error when watcher runs before mounted()
            this.renderChart(this.plot.data, this.plot.options);
        }
    }
}

In this project I am required to use Typescript. The only example for using vue-chartjs with Typescript utilizes the Component syntax, so this is how I implemented it above.

The offending line is this:

export default class BasePlot extends Scatter {

This all works just fine. However, it throws these errors to the console whenever the chart is created:

vue.esm.js:628 [Vue warn]: Error in data(): "TypeError: _super.apply is not a function"

found in

---> <BasePlot>
       <CalcZ> at Scripts/app/components/capacitor/simulation/CalcZ.vue
         <App> at Scripts/app/components/App.vue
           <Root>
warn @ vue.esm.js:628
logError @ vue.esm.js:1893
globalHandleError @ vue.esm.js:1888
handleError @ vue.esm.js:1848
getData @ vue.esm.js:4753
initData @ vue.esm.js:4708
initState @ vue.esm.js:4645
Vue._init @ vue.esm.js:5009
VueComponent @ vue.esm.js:5157
createComponentInstanceForVnode @ vue.esm.js:3292
init @ vue.esm.js:3123
createComponent @ vue.esm.js:5983
createElm @ vue.esm.js:5930
createChildren @ vue.esm.js:6058
createElm @ vue.esm.js:5959
createChildren @ vue.esm.js:6058
createElm @ vue.esm.js:5959
createChildren @ vue.esm.js:6058
createElm @ vue.esm.js:5959
patch @ vue.esm.js:6482
Vue._update @ vue.esm.js:3948
updateComponent @ vue.esm.js:4069
get @ vue.esm.js:4482
Watcher @ vue.esm.js:4471
mountComponent @ vue.esm.js:4076
Vue.$mount @ vue.esm.js:9057
Vue.$mount @ vue.esm.js:11953
init @ vue.esm.js:3127
merged @ vue.esm.js:3310
createComponent @ vue.esm.js:5983
createElm @ vue.esm.js:5930
createChildren @ vue.esm.js:6058
createElm @ vue.esm.js:5959
createChildren @ vue.esm.js:6058
createElm @ vue.esm.js:5959
createChildren @ vue.esm.js:6058
createElm @ vue.esm.js:5959
patch @ vue.esm.js:6482
Vue._update @ vue.esm.js:3948
updateComponent @ vue.esm.js:4069
get @ vue.esm.js:4482
Watcher @ vue.esm.js:4471
mountComponent @ vue.esm.js:4076
Vue.$mount @ vue.esm.js:9057
Vue.$mount @ vue.esm.js:11953
init @ vue.esm.js:3127
createComponent @ vue.esm.js:5983
createElm @ vue.esm.js:5930
patch @ vue.esm.js:6521
Vue._update @ vue.esm.js:3948
updateComponent @ vue.esm.js:4069
get @ vue.esm.js:4482
Watcher @ vue.esm.js:4471
mountComponent @ vue.esm.js:4076
Vue.$mount @ vue.esm.js:9057
Vue.$mount @ vue.esm.js:11953
eval @ index.ts:21
./Scripts/app/index.ts @ app.js:2185
__webpack_require__ @ app.js:727
fn @ app.js:101
0 @ app.js:6298
__webpack_require__ @ app.js:727
(anonymous) @ app.js:794
(anonymous) @ app.js:797
Show 34 more frames

vue.esm.js:1897 TypeError: _super.apply is not a function
    at new BasePlot (base-plot.ts:15)
    at collectDataFromConstructor (vue-class-component.esm.js:97)
    at VueComponent.data (vue-class-component.esm.js:172)
    at VueComponent.mergedDataFn (vue.esm.js:1228)
    at getData (vue.esm.js:4751)
    at initData (vue.esm.js:4708)
    at initState (vue.esm.js:4645)
    at VueComponent.Vue._init (vue.esm.js:5009)
    at new BasePlot (vue.esm.js:5157)
    at createComponentInstanceForVnode (vue.esm.js:3292)
elken commented 4 years ago

Use the following pattern:

import { ChartData, ChartOptions } from "chart.js";
...
interface BasePlot extends Vue {
  renderChart(data: ChartData, options?: ChartOptions): void;
}
...

@Component({
    extends: Scatter,
})
class BasePlot extends Scatter {
...
}

export default BasePlot;
4b11b4 commented 4 years ago

Hmm... this didn't seem to solve my issue. I still get the error in the console.

In your example above... the interface is merged with the class? They are both named BasePlot

elken commented 4 years ago
@Component({
    extends: Bar
})
export default class BasePlot extends Vue<Bar> {
...
}

is the documented way but the above solution also works.

Yes, Typescript merges them together (T ∪ U).

4b11b4 commented 4 years ago

@elken the method directly above removes the error, but I need to put a // @ts-ignore above the @Component({extends: Scatter}) otherwise I get TS2345: Argument of type 'typeof BasePlot' is not assignable to parameter of type 'VueClass<Vue>'. Type 'typeof BasePlot' is not assignable to type 'VueConstructor<Vue>'. Types of parameters 'options' and 'options' are incompatible. Type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>> | undefined' is not assignable to type 'ThisTypedComponentOptionsWithArrayProps<Vue, Scatter, object, object, never> | undefined'. Type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>>' is not assignable to type 'ThisTypedComponentOptionsWithArrayProps<Vue, Scatter, object, object, never>'. Type 'ComponentOptions<Vue, DefaultData<Vue>, DefaultMethods<Vue>, DefaultComputed, PropsDefinition<Record<string, any>>, Record<...>>' is not assignable to type 'ComponentOptions<Vue, DataDef<Scatter, Record<never, any>, Vue>, object, object, never[], Record<never, any>>'. Types of property 'data' are incompatible. Type 'object | ((this: Vue) => object) | undefined' is not assignable to type 'Scatter | ((this: Readonly<Record<never, any>> & Vue) => Scatter) | undefined'. Type 'object' is not assignable to type 'Scatter | ((this: Readonly<Record<never, any>> & Vue) => Scatter) | undefined'. Type 'object' is not assignable to type '(this: Readonly<Record<never, any>> & Vue) => Scatter'. errors @ client:159 eval @ socket.js:47 sock.onmessage @ SockJSClient.js:63 EventTarget.dispatchEvent @ sockjs.js:170 eval @ sockjs.js:888 SockJS._transportMessage @ sockjs.js:886 EventEmitter.emit @ sockjs.js:86 WebSocketTransport.ws.onmessage @ sockjs.js:2962

and on the next line Vue<Scatter> gives an error that "Type 'BasePlot' cannot extend 'Data & Methods & Computed & {[P in PropNames]: any} & Vue' which is not a constructor function type."

...but otherwise everything seems to be functioning the same without the errors to the console.

elken commented 4 years ago

Probably related to

export interface IPlot {
    data: Object,
    options: Object,
}

When it expects ChartData and ChartOptions? now.

4b11b4 commented 4 years ago

Hmm... good catch... that was some very old code.

I updated that interface to be ChartData and ChartOptions instead of Object for the types, but it doesn't seem to be the problem.

elken commented 4 years ago

Don't know what else to suggest then. See my working code below and try and make it match.


interface Graph extends Vue {
  renderChart(data: ChartData, options?: ChartOptions): void;
}

@Component({
  extends: Bar,
  computed: {
    ...mapGetters({
      isDarkMode: "isDarkMode"
    })
  }
})
class Graph extends Vue {
  @Prop({ default: null }) chartdata: ChartData;
  @Prop({ default: null }) options: ChartOptions;

  mounted() {
    this.renderChart(this.chartdata, {
      ...getSettings(),
      ...this.options
    });
  }

  @Watch("isDarkMode")
  onDarkModeToggle() {
    this.renderChart(this.chartdata, {
      ...getSettings(),
      ...this.options
    });
  }
}

getSettings() is just a method to return my settings, I have a number of computed properties in it.