question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

this.$t is not a function in a class component

See original GitHub issue

Subject of the issue

I have the following component

<template>
  <div>{{someData}}</div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  private someData = this.$t("message");
}
</script>

and the following unit test

import { mount } from '@vue/test-utils'
import App from '@/App.vue'

describe('App.vue', () => {
  it('some unit test', () => {
    const wrapper = mount(App, {
      mocks: {
        $t: (key: string) => key
      }
    });
    expect(wrapper).toBeDefined();
  })
})

When running this unit test, it fails with the following error:

 FAIL  tests/unit/example.spec.ts
  HelloWorld.vue
    ✕ some unit test (22ms)

  ● HelloWorld.vue › some unit test

    TypeError: this.$t is not a function

       8 | @Component
       9 | export default class App extends Vue {
    > 10 |   private someData = this.$t("message");
         | ^
      11 | }
      12 | </script>
      13 | 

      at new App (src/App.vue:10:1)
      at collectDataFromConstructor (node_modules/vue-class-component/dist/vue-class-component.common.js:165:14)
      at VueComponent.data (node_modules/vue-class-component/dist/vue-class-component.common.js:227:14)
      at getData (node_modules/vue/dist/vue.runtime.common.dev.js:4732:17)
      at initData (node_modules/vue/dist/vue.runtime.common.dev.js:4689:7)
      at initState (node_modules/vue/dist/vue.runtime.common.dev.js:4628:5)
      at VueComponent.Vue._init (node_modules/vue/dist/vue.runtime.common.dev.js:4987:5)
      at new VueComponent (node_modules/vue/dist/vue.runtime.common.dev.js:5134:12)
      at createComponentInstanceForVnode (node_modules/vue/dist/vue.runtime.common.dev.js:3277:10)
      at init (node_modules/vue/dist/vue.runtime.common.dev.js:3108:45)
      at createComponent (node_modules/vue/dist/vue.runtime.common.dev.js:5958:9)
      at createElm (node_modules/vue/dist/vue.runtime.common.dev.js:5905:9)
      at VueComponent.patch [as __patch__] (node_modules/vue/dist/vue.runtime.common.dev.js:6455:7)
      at VueComponent.Vue._update (node_modules/vue/dist/vue.runtime.common.dev.js:3933:19)
      at VueComponent.updateComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4054:10)
      at Watcher.get (node_modules/vue/dist/vue.runtime.common.dev.js:4465:25)
      at new Watcher (node_modules/vue/dist/vue.runtime.common.dev.js:4454:12)
      at mountComponent (node_modules/vue/dist/vue.runtime.common.dev.js:4061:3)
      at VueComponent.Object.<anonymous>.Vue.$mount (node_modules/vue/dist/vue.runtime.common.dev.js:8392:10)
      at mount (node_modules/@vue/test-utils/dist/vue-test-utils.js:13991:21)
      at Object.<anonymous> (tests/unit/example.spec.ts:6:21)

Steps to reproduce

Just run unit tests in this project: i18n-test.zip

Expected behaviour

Error this.$t is not a function should not occur. The $t function is mocked.

Actual behaviour

An error this.$t is not a function is thrown.

Additional context

If I convert someData to computed property, like this:

<template>
  <div>{{someData}}</div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';

@Component
export default class App extends Vue {
  private get someData() {
    return this.$t("message");
  }
</script>

the error is gone.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Comments:13 (8 by maintainers)

github_iconTop GitHub Comments

2reactions
lmiller1990commented, Jan 24, 2021

Ok… this worked (webpack + mocha combo).

import { expect } from 'chai'
import Vue from 'vue'
import { mount, createLocalVue } from '@vue/test-utils'
import App from '@/App.vue'

class i18n {
  static install(Vue: any) {
    Vue.prototype.$t = (key: string) => key
  }
}

Vue.use(i18n)

describe('HelloWorld.vue', () => {
  it('renders props.msg when passed', () => {
    const wrapper = mount(App)
    console.log(wrapper.html())
  })
})

But this does not:

import { expect } from 'chai'
import Vue from 'vue'
import { mount, createLocalVue } from '@vue/test-utils'
import App from '@/App.vue'

class i18n {
  static install(Vue: any) {
    Vue.prototype.$t = (key: string) => key
  }
}

const localVue = createLocalVue()
localVue.use(i18n)

describe('HelloWorld.vue', () => {
  it('renders props.msg when passed', () => {
    const wrapper = mount(App, {
      localVue
    })
    console.log(wrapper.html())
  })
})

I guess when VCC does it’s thing, Vue already has $t. It does not work with localVue - VCC does not know about localVue. I don’t see any way to “tell” VCC which Vue instance to use, so this is likely out of the question.

If you dont’t mind using a mutated global Vue for all your tests, you could do something like I’ve shown here (making a “fake” i18n plugin).

I am still not sure on how we can make mocks work with VCC. Let’s experiment a bit more.

1reaction
ktsncommented, Jan 24, 2021

A repro excluded the dep to vue-test-utils would be:

    @Component
    class Base extends Vue {
      test() {
        return 'base'
      }
    }

    @Component
    class Test extends Base {
      result = this.test()
    }

    const vm = new Test()
    expect(vm.result).to.equal('base')

    const Replaced = Vue.extend()
    Replaced.prototype.test = () => 'replaced'

    const replaced: Test = new (Replaced.extend({
      ...(Test as any).options,
    }))()
    expect(replaced.result).to.equal('replaced')

We could replace the prototype of the original constructor with the one of localVue in collectDataFromConstructor but I’m not very sure about it and if it is really a good idea. I’ll need more time to take a look…

Read more comments on GitHub >

github_iconTop Results From Across the Web

this.$t is not a function in a class component #1767
I guess this is something which needs to be fixed in vue-class-component . I have this issue when running the app as well,...
Read more >
Global method this.$t from vue-i18n does not work in function
convertErrors has no "this", it's not bound to a component. Move it into the component's methods in order to reference this :
Read more >
What does "TypeError: this[t] is not a function" mean? - Reddit
Your code has been compiled by a builder, like webpack or vite. Unless there is a backtrace with the error (possibly enhanced with...
Read more >
TypeError: "x" is not a function - JavaScript - MDN Web Docs
Maybe there is a typo in the function name? Maybe the object you are calling the method on does not have this function?...
Read more >
Developers - this.$t is not a function in a class component -
t is not a function in a class component. ... Vue } from 'vue-property-decorator'; @Component export default class App extends Vue { private...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found