2020 update

Recently, I re-read this article, and I found that this article did not indicate ts pre-release pre-highlight, so I added pre-highlight. In addition, the environment of writing this article is Vue 2.5.*, which may not be suitable for the current environment, there are some plug-ins or some implementation methods, there may be some changes. Or Vue officially has a different implementation, so this article is still for reference, after all, this article was written in 18 years.

I’ve been trying TypeScript for a while, trying to convert a Vue project to TypeScript, and it’s been nice. Vue doesn’t work perfectly with TypeScript at the moment, Vuex doesn’t work perfectly with TypeScript, so be careful. If you want to give it a try, it’s highly recommended that you use Vue-CLI 3 to generate the project directory directly. For example, with tsconfig, choose TypeScript when using vue-CLI.

See the typescript-vue-starter documentation if you want to experience configuration from 0

The initial configuration is not mentioned here, because vue-cli is already configured by default. In the root directory, there is a tsconfig.json file, and in the SRC folder, there are shims-tsx.d.ts, shims-vue.d.ts files

The shims-vue. D. Ts file is used in TypeScript to recognize vue files. By default, Ts does not support importing vue files. This file tells Ts to import vue files as VueConstructor< vue >. So to import a vue file you have to write a.vue suffix, but this will also cause, even if you write the wrong path to the imported.vue file, static detection will not detect the error, if you put your mouse over it you will see that the wrong path is pointing to that file, Because you defined the module, all.vue imports will point to this file, but if your path is correct, ts will read the correct module. It may not help to say so much, but take a look at the following two pictures

The import path is correct
The import path is incorrect

If you do not use JSX syntax, you can ignore this, but it is highly recommended to use JSX syntax. After all, templates do not get static typing hints. Of course, if you are advanced, Vue render function If you want to use JSX syntax, you can have a better experience with babel-Plugin-JSX-V-Model, which implements v-Model in JSX syntax.

It was good to understand the above things, basic need not change

Editor support

Visual Studio Code and Vetur are recommended, and WebStorm is recommended if you are using an IDE

There are two main ways to write Vue in TypeScriptVue.extend()vue-class-component

  • Vue.extend(): Creates a “subclass” using the base Vue constructor. This approach is the closest to Vue’s single-file component. If a perfect Vue project changes from JS to TS, it’s quick to use this approach. Just add lang= TS and some necessary variable types, and wrap it with vue.extend (). Look at the following example: JavaScript

    JavaScript


    TypeScript

    TypeScript


    You can see there’s not much change, right

  • Vue-class-component: Usually used in conjunction with vue-property-decorators. Use only vue-property-decorators. Vue-property-decorator is an extension of the Vue-class-component and provides a number of decorators such as @prop, @watch, and so on. It is possible to write class-like components using this, but if you are a full project JS or TS, There’s a lot to change. Take a look at the following example: JavaScript

    JavaScript


    TypeScript

    TypeScript


    You can see how much it’s changed

These two writing styles are very different, and the following are specific implementations of the two methods

  • Component props

    • Vue.extend() implementations of props are really no different than JavaScript, but if you need an Object variable type hint, it’s a little different

    • The vue-class-component implementation of props requires the introduction of Prop from the Vue-property-decorator. It is also very convenient to use the same example as above

            // JavaScript
            props: ['isVisible'.'title'.'item'.'count'.'items']
            // If prop validation is added, write this
            props: {
              isVisible: {
                type: Boolean,
                required: true
              },
              title: {
                type: [String.Number]
              },
              item: {
                type: Object
              },
              items: {
                type: Array,
              }
              count: {
                count: Number}}// TypeScript
            /* This does not change anything, but there is no type hint. These variables are recognized by TS, but they are all recognized as any, as without type checking */
            props: ['isVisible'.'title'.'item'.'count'.'items']
            /* When the prop validation is added, TS will indicate the type of the prop. If it is an object, TS will also indicate the member of the object. The method is similar to that of JS, but the object type (including objects, arrays and functions) is different. * /
            // Suppose the structure of the item object is
            interface Item {
              key: string
              val: string
              num: number
            }
            props: {
              isVisible: {
                type: Boolean,
                required: true
              },
              title: {
                type: [String.Number]
              },
              item: {
                // This is not the case
                // Object as Item
                type: Object as () => Item
              },
              itmes: {
                // This is not the case
                // Array as Array<Item>
                type: Array as() = >Array<Item>
              }
              count: {
                count: Number}}/ / the vue - class - component
            import { Vue, Component, Prop } from 'vue-property-decorator'
      
            // Make an assertion that is not null! Otherwise, of course, you define it as any and forget about it
            / * [non-null-assertion-operator](https://github.com/Microsoft/TypeScript/wiki/What's-new-in-TypeScript#non-null-assertion-o Perator) refer to this */ for non-empty assertions
            @Prop({
              required: true}) isVisible! :boolean
            @Prop() title! :string | number
            @Prop() item! : Item@Prop() items! :Array<Item>
            @Prop() count! :number
      
      Copy the code
  • Component Data Computed Methods Watch

    • Vue.extend() implements data in the same way as JavaScript, and for computed, there is no significant change, but for computed, you must specify the type of the return value, otherwise you will get an error. Methods are processed in the same way as computed. For this, the return type must be specified. The same is true for watch

    • For vue-class-Component, if you implement data, you just write the variables in the class. For computed, you write the getters and setters. For methods, you write the methods in the class. Watch needs to import the watch modifier from the Vue-property-decorator

            // A simple example
            // Vue.extend()
            import Vue from 'vue'
            export default Vue.extend({
              data() {
                return {
                  count: 1,
                  item: {
                    c: ' ',
                    n: ' '
                  }
                }
              },
              computed: {
                // We need to mark the return value type with 'this' involved in the operation
                num(): number {
                  return this.count
                },
                name: {
                  // We need to mark the return value type with 'this' involved in the operation
                  get() :string {
                    return this.item.n
                  },
                  set(val: string) {
                    this.item.n = val
                  }
                }
              },
              watch: {
                count(newVal: number, oldVal: number) :void {
                  console.log(newVal)
                },
                'item.n'(newVal: string, oldVal: string) :void {
                  console.log(newVal)
                },
                item: {
                  handler(newV, oldVal) {
                    console.log(oldVal)
                  },
                  deep: true
                }
              },
              methods: {
                reset(): void {
                  this.$emit('reset')
                },
                getKey(): string {
                  return this.item.c
                }
              }
            })
          // vue-class-component
          import { Vue, Component, Watch } from 'vue-property-decorator'
      
          interface KeyValue {
            c: string
            n: string
          }
      
          @Component
          export default class Test extends Vue {
            // data
            count: number = 1
            item: KeyValue = {
              c: ' ',
              n: ' '
            }
      
            // computed
            get num(): number {
              return this.count
            }
            get name(): string {
              return this.item.n
            }
            // Note that there is no return type, not even void
            set name(val: string) {
              this.item.n = val
            }
      
            // watch
            @Watch('count')
            watchCount(newVal: number, oldVal: number) :void {
              console.log(newVal)
            }
            @Watch('item.n')
            watchName(newVal: string, oldVal: string) :void {
              console.log(newVal)
            }
            @Watch('item', { deep: true })
            watchItem(newVal: KeyValue, oldVal: KeyValue): void {
              console.log(newVal)
            }
            // methods
            reset(): void {
              this.$emit('reset')
            },
            getKey(): string {
              return this.item.c
            }
          }
      
      Copy the code
  • Component components

    • Vue.extend() Components is written exactly the same as in JavaScript

    • Vue-class-component needs to write imported Components inside the decorator @components ({})

            // Vue.extend
            import Vue from 'vue'
            import MainHeader from './header.vue'
            import MainContent from './content.vue'
      
            export default Vue.extend({
              components: {
                MainHeader,
                MainContent
              }
            })
      
            // vue-class-component
            import { Vue, Component } from 'vue-property-decorator'
            import MainHeader from './header.vue'
            import MainContent from './content.vue'
      
            @Component({
              components: {
                MainHeader,
                MainContent
              }
            })
            export default class extends Vue {}
          ` ``
      Copy the code
  • Component mixins

    • Vue.extend() does not allow for a full mix of mixins, only one. It is not recommended to write in this way because multiple inheritance is not possible. If you must try this writing method, you can look at this Issue, I have not tried this writing method, but someone wrote an example, can be used as a reference, but I tried but failed

          // ExampleMixin.vue
          export default Vue.extend({
            data () {
              return {
                testValue: 'test'}}})// other.vue
          export default Vue.extend({
            mixins: [ExampleMixin],
            created () {
              this.testValue // Error, testValue does not exist!}}) We need to modify this slightly:// other.vue
          export default ExampleMixin.extend({
            mixins: [ExampleMixin],
            created () {
              this.testValue // Compile passed}})Copy the code
    • Vue-class-component enables multiple blending and is written like class inheritance

            // mixin1.ts
            import Vue from 'vue'
      
            export default Vue.extend({
              data () {
                return {
                  valFromMixin1: 'test'}}})/ / can't be
            Mixin1 is not a constructor function type
            export default {
              data () {
                return {
                  valFromMixin1: 'test'}}}// mixin2.ts
            import { Component, Vue } from 'vue-property-decorator'
      
            @Component
            export default class Mixin2 extends Vue {
              methodFromMixin2() {}
            }
      
            // test.ts
            import Mixin1 from './mixin1'
            import Mixin2 from './mixin2'
            import { Component, Mixins } from 'vue-property-decorator'
      
            export default class Test extends Mixins(Mixin1, Mixin2) {
              test() {
                this.methodFromMixin2()
                console.log(this.valFromMixin1)
              }
            }
            // If only one is mixed in, you can write this
            export default class Test extends Mixin1 {}
            export default class Test extends Mixin2 {}
          ` `'Not only will not write this error, and the editor also prompt! [Alt ](https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2018/10/29/166be51e4d68dd4a~tplv-t2oaga2asx-image.image SetWatch is a method in BookingWatcher. In fact, the Test class has no properties of its own. It inherits from Vue, BookingWatcher, and TestMixinsCopy the code
  • Functional component

    This can only be used with vue.extends (), but not with vue-class-Component. Take a look at this Issue that provides TypeScript for functional components

This part of the information reference

Some thoughts on using TypeScript in Vue

Summary of some problems encountered in development

  • $refs error

    $refs error this question basic have believe, unless you are good to this, as shown in figure:

    Error message is the Property ‘blur’ does not exist on a type Element ‘Vue | | Vue [] | Element []’. The Property ‘blur’ does not exist on the type ‘Vue’.

    Solution: Change the error section of the picture above to

      // Change this variable to define HTMLInputElement, where type assertion is required
      test() {
        let inputBox: HTMLInputElement = this.$refs.inputBox as HTMLInputElement
         inputBox.blur()
      }
    Copy the code

    $refs! $refs! $refs! : {}

    The editor will also tell you what methods are in the component when you type this.$refs.header. “, the editor prompts for properties and methods in the Header component