What do I need to do when I first use TypeScript in a Vue project

Preface

As we all know, the new version 3.0 of Vue uses TypeScript development, which makes the already popular TypeScript attract more people's attention. Although TypeScript has only been popular in recent years, it was actually born in October 2012, and the official version was released in June 2013. It is a free and open source programming language written by Microsoft. TypeScript is a superset of JavaScript, which extends the syntax of JavaScript, adds optional static types and class based object-oriented programming.

The common mistake in JavaScript development is that variables or properties do not exist, but these are low-level errors, and static type checking can make up for this shortcoming. What is static type? Here's a chestnut:

//javascript 
let str = 'hello'
str = 100 //ok

//typescript
let str:string = 'hello'
str = 100 //error: Type '100' is not assignable to type 'string'.

You can see that TypeScript needs to add types to variables when declaring variables, and a warning will be thrown if the values and types of variables are inconsistent. Static types are only checked at compile time, and the final compiled code is still JavaScript. Even if we assign a variable of type string to another type, the code will work.

Secondly, TypeScript increases the readability and maintainability of the code. Type definition is actually a good document. For example, when using a function, you only need to look at the type definitions of parameters and return values to know how the function works.

Catalog

Preparation

npm

Install typescript

npm install typescript @vue/cli-plugin-typescript -D

New file

Create shims-vue.d.ts, shims-tsx.d.ts, tsconfig.json in the root directory of the project

  • shims-vue.d.ts
import Vue from 'vue';

declare module '*.vue' {
  export default Vue;
}
  • shims-tsx.d.ts
import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    type Element = VNode
    type ElementClass = Vue
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}
  • tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "module": "esnext",
    "strict": true,
    "jsx": "preserve",
    "importHelpers": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators":true,
    "sourceMap": true,
    "noImplicitThis": false,
    "baseUrl": ".",
    "types": [
      "webpack-env"
    ],
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "tests/**/*.ts",
    "tests/**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

ESLint configuration

Why ESLint instead of TSLint?

In January, TypeScript's official blog recommended using ESLint instead of TSLint. The ESLint team will no longer maintain the TypeScript ESLint parser and will not publish it on Npm. Any user using the TypeScript ESLint parser should use @ TypeScript ESLint / parser instead.

Official explanation:

We have noticed that there are some architecture problems that affect the performance of TSLint rules. ESLint already has the higher performance architecture we hope to get from linter. In addition, different user communities often have lint rules (such as React hook or Vue rules) built for ESLint rather than TSLint. In view of this, our editorial team will focus on leveraging ESLint rather than replicating the work. For scenarios that ESLint does not currently cover (such as semantic linking or program wide linking), we will strive to equate the TypeScript support of ESLint with TSLint.

original text

How to use

AlloyTeam provides a comprehensive set of EsLint configuration specifications, which are applicable to React/Vue/Typescript projects, and on this basis, rules can be customized.
GitHub

install

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-alloy

View description of configuration item AlloyTeam ESLint rules

To configure

Create. eslintrc.js in the root directory of the project and copy the following to it:

module.exports = {
  extends: [
    'alloy',
    'alloy/typescript',
  ],
  env: {
    browser: true,
    node: true,
  },
  rules: {
    // Custom rules
    'spaced-comment': 'off',
    '@typescript-eslint/explicit-member-accessibility': 'off',
    'grouped-accessor-pairs': 'off',
    'no-constructor-return': 'off',
    'no-dupe-else-if': 'off',
    'no-import-assign': 'off',
    'no-setter-return': 'off',
    'prefer-regex-literals': 'off'
  }
};

supplement

If you want to know more about configuration items, you can go to ESLint official website Search for configuration items.

If VScode is used, it is recommended to use the ESLint plug-in to assist development.

File transformation

Entry file

  1. Change main.js to main.ts
  2. vue.config.js modify the entry file
const path = require('path')
module.exports = {
  ...
  pages: {
    index: {
      entry: path.resolve(__dirname+'/src/main.ts')
    },
  },
  ...
}

vue component file

With the introduction of classes in TypeScript and ES6, we need additional features to support annotation or modification of classes and their members in some scenarios. Decorators provide a way for us to add annotations through metaprogramming syntax on class declaration and members.

Vue also provides us with a TypeScript decorator for class style components. Before using the decorator, you need to set experiential decorators to true in tsconfig.json.

Installing the vue finisher

The Vue property decorator library completely depends on Vue class component, which should be installed together during installation

npm install vue-class-component vue-property-decorator -D

Transformation of.vue

You only need to modify the contents of srcipt, and no other changes are needed

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

@Component({
  created(){
    
  },
  components:{
    draggable
  }
})
export default class MyComponent extends Vue {
  /* data */
  private ButtonGrounp:Array<any> = ['edit', 'del']
  public dialogFormVisible:boolean = false
  
  /*method*/
  setDialogFormVisible(){
    this.dialogFormVisible = false
  }
  addButton(btn:string){
    this.ButtonGrounp.push(btn)
  }

  /*compute*/
  get routeType(){
    return this.$route.params.type
  }
}
</script>

Class member modifier. If no modifier is added, it defaults to public

  • Public: public, with free access to the members of the class
  • Protected: protected. Classes and their inherited subclasses can be accessed
  • Private: private, accessible only to classes

Prop

!: use explicit assignment assertion modifiers for properties. Learn more File

import { Component, Vue, Prop } from "vue-property-decorator";
export default class MyComponent extends Vue {
  ...
  @Prop({type: Number,default: 0}) readonly id!: number
  ...
}

Equate to

export default {
  ...
  props:{
    id:{
      type: Number,
      default: 0
    }
  }
  ...
}

Watch

import { Component, Vue, Watch } from "vue-property-decorator";
export default class MyComponent extends Vue {
  ...
  @Watch('dialogFormVisible')
  dialogFormVisibleChange(newVal:boolean){
    // Some operations
  }
  ...
}

Equate to

export default {
  ...
  watch:{
    dialogFormVisible(){
      // Some operations
    }
  }
  ...
}

Provide/Inject

// App.vue
import {Component, Vue, Provide} from 'vue-property-decorator'
@Component
export default class App extends Vue {
  @Provide() app = this
}

// MyComponent.vue
import {Component, Vue, Inject} from 'vue-property-decorator'
@Component
export default class MyComponent extends Vue {
  @Inject() readonly app!: Vue
}

Equate to

// App.vue
export default {
  provide() {
    return {
      'app': this
    }
  }
}

// MyComponent.vue
export default {
  inject: ['app']
}

For more decorator use, refer to Vue property decorator document

Global declaration

*. d.ts file

At present, the mainstream library files are written in JavaScript. TypeScript is a superset of JavaScript. In order to support the type definition of these libraries, type definition files (*. d.ts) are provided. Developers write type definition files and publish them to npm. When users need to use the library in TypeScript projects, they can download this package separately, so that JS libraries can be used in TypeScript projects Function.

For example, md5 is believed to have been used by many people. This library can turn strings into a string of hash values, which is irreversible. It is often used to hash sensitive information and send it to the back-end for verification to ensure data security. If we want to use it in the TypeScript project, we need to download @ tyeps/md5 additionally. You can see the types defined for md5 in index.d.ts of this folder.

/// <reference types="node" />

declare function md5(message: string | Buffer | Array<number>): string;

declare namespace md5 {}

export = md5;

How TypeScript recognizes *. d.ts

TypeScript will recognize the. d.ts file automatically when the project is compiled. All we need to do is write. d.ts, and then TypeScript will inject these written type definitions into the global for use.

Add properties / methods for vue instances

When we use this.$route or some methods on the prototype, typescript cannot infer. When compiling, it will report an error that the property $route does not exist. We need to add a global declaration for these global properties or methods

Modify shims-vue.d.ts, and of course you can choose to customize *. D.ts to add the declaration

import Vue from 'vue';
import VueRouter, { Route } from 'vue-router'

declare module '*.vue' {
  export default Vue;
}

declare module 'vue/types/vue' {
  interface Vue {
    $api: any;
    $bus: any;
    $router: VueRouter;
    $route: Route;
  }
}

Custom type definition file

When some types or interfaces need to be used frequently, we can write global type definitions for the project,
Create @ types folder in the root path, where *. d.ts files are stored. It is specially used to manage the type definition files in the project.

Here I define a global.d.ts file:

//declare can create variables in *. d.ts file. declare can only be the outermost scope
//variable
declare var num: number;

//type
type StrOrNum = string | number

//function
declare function handler(str: string): void;

// class
declare class User { 
  
}

//Interface
interface OBJ {
  [propName: string]: any;
  [propName: number]: any;
}

interface RES extends OBJ {
  resultCode: number;
  data: any;
  msg?: string;
}

Hands free, transvue2ts conversion tool

The most troublesome part of the transformation process is the grammar conversion. The contents are all fixed writing methods. These repetitive and boring work can be done by the machine. Here we can use transvue2ts tool to improve efficiency. Transvue2ts will help us convert data, prop, watch and other syntax into decorator syntax.

install

npm i transvue2ts -g

Use

After installation, the path of the transvue2ts library will be written to the path of the system. You can directly open the command line tool to use. The second parameter of the command is the complete path of the file.
After the command is executed, a new converted file will be generated in the same level directory, such as index.vue under the view folder, and indexTS.vue will be generated after the conversion.

Processing single file component

transvue2ts D:\typescript-vue-admin-demo\src\pages\index.vue
=>
//Output path: D: \ typescript Vue admin demo \ SRC \ pages \ indexts.vue

Process all vue component files in the folder

transvue2ts D:\typescript-vue-admin-demo\src\pages
=>
//Output path: D: \ typescript Vue admin demo \ SRC \ pages

supplement

Don't think that having tools really liberates our hands. Tools only help us to change part of our grammar. The syntax and the type definition of the parameter that the tool failed to handle still need to be modified. Note that comments are filtered out after conversion.

The author of this tool Nuggets Introduction and implementation of tools

About the use of third-party libraries

Some three-party libraries include type definition files when they are installed. When they are used, they do not need to define by themselves. They can directly use the officially provided type definition.

The corresponding package folder can be found in node modules. Type files are generally stored in the types folder. In fact, type definition files are just like documents. These contents can clearly see the required parameters and parameter types.

Here are some examples of using a tripartite Library in Vue:

Element UI component parameters

Use type definition

import { Component, Vue } from "vue-property-decorator";
import { ElLoadingComponent, LoadingServiceOptions } from 'element-ui/types/loading'

let loadingMark:ElLoadingComponent; 
let loadingConfig:LoadingServiceOptions = {
  lock: true,
  text: "Loading",
  spinner: "el-icon-loading",
  background: "rgba(255, 255, 255, 0.7)"
};

@Component
export default class MyComponent extends Vue {
  ...
  getList() {
    loadingMark = this.$loading(loadingConfig);
    this.$api.getList()
      .then((res:RES) => {
        loadingMark.close();
      });
  }
  ...
}

Element UI / types / loading. There are many comments in the original file to describe each attribute

export interface LoadingServiceOptions {
  target?: HTMLElement | string
  body?: boolean
  fullscreen?: boolean
  lock?: boolean
  text?: string
  spinner?: string
  background?: string
  customClass?: string
}
export declare class ElLoadingComponent extends Vue {
  close (): void
}
declare module 'vue/types/vue' {
  interface Vue {
    $loading (options: LoadingServiceOptions): ElLoadingComponent
  }
}

Vue router hook function

Use type definition

import { Component, Vue } from "vue-property-decorator";
import { NavigationGuard } from "vue-router";

@Component
export default class MyComponent extends Vue {
  beforeRouteUpdate:NavigationGuard = function(to, from, next) {
    next();
  }
}

In Vue router / types / router. D.ts, you can see the type definition of hook function at the beginning.

export type NavigationGuard<V extends Vue = Vue> = (
  to: Route,
  from: Route,
  next: (to?: RawLocation | false | ((vm: V) => any) | void) => void
) => any

There are also routers and routes used previously. All methods, properties and parameters are clearly described here

export declare class VueRouter {
  constructor (options?: RouterOptions);

  app: Vue;
  mode: RouterMode;
  currentRoute: Route;

  beforeEach (guard: NavigationGuard): Function;
  beforeResolve (guard: NavigationGuard): Function;
  afterEach (hook: (to: Route, from: Route) => any): Function;
  push (location: RawLocation, onComplete?: Function, onAbort?: ErrorHandler): void;
  replace (location: RawLocation, onComplete?: Function, onAbort?: ErrorHandler): void;
  go (n: number): void;
  back (): void;
  forward (): void;
  getMatchedComponents (to?: RawLocation | Route): Component[];
  onReady (cb: Function, errorCb?: ErrorHandler): void;
  onError (cb: ErrorHandler): void;
  addRoutes (routes: RouteConfig[]): void;
  resolve (to: RawLocation, current?: Route, append?: boolean): {
    location: Location;
    route: Route;
    href: string;
    normalizedTo: Location;
    resolved: Route;
  };

  static install: PluginFunction<never>;
}
export interface Route {
  path: string;
  name?: string;
  hash: string;
  query: Dictionary<string | (string | null)[]>;
  params: Dictionary<string>;
  fullPath: string;
  matched: RouteRecord[];
  redirectedFrom?: string;
  meta?: any;
}

Custom tripartite library declaration

When the three-party library used does not have a *. d.ts declaration file, this error will be reported when compiling the project:

 Could not find a declaration file for module 'vuedraggable'. 'D:/typescript-vue-admin-demo/node_modules/vuedraggable/dist/vuedraggable.umd.min.js' implicitly has an 'any' type.
  Try `npm install @types/vuedraggable` if it exists or add a new declaration (.d.ts) file containing `declare module 'vuedraggable';`

Roughly speaking, vuedraggable cannot find the declaration file. You can try to install @ types / vuedraggable (if it exists), or customize the new declaration file.

Install @ types/vuedraggable

Follow the prompts to select the first method, install @ types/vuedraggable, and then find the Error 404 not found, indicating that this package does not exist. I think this component is still used by many people (the Weekly Download volume is 18w). I didn't expect that the community didn't declare the file.

Custom claim file

But I can only choose the second way. To be honest, I have also explored a little time (I don't know much about this aspect, and I'm not familiar with it)

First, create the vuedraggable folder under node_modules/@types. If there is no @ types folder, you can create it yourself. Create index.d.ts under the vuedraggable folder. Write the following:

import Vue from 'vue'
declare class Vuedraggable extends Vue{}
export = Vuedraggable

No error is reported after recompilation, so the problem is solved.

Suggestions and precautions

Transformation process

  • When accessing TypeScript, you don't need to change all files to ts syntax at one time. The original syntax can also work normally. It's better to modify them individually
  • It is normal that a large number of errors occur during the initial transformation, basically all of which are type errors. Translate according to the error prompts to correct the corresponding errors
  • When importing ts files, you do not need to add the. ts suffix
  • After defining global variables for the project, it cannot be used normally. Run the server again (I have encountered...)

Encounter problems

  • Search engine oriented, if you know where the problem is
  • Look at the documents carefully. Most of the errors are relatively basic. Documents can solve problems
  • Github looks for TypeScript related projects to see how others write

Written in the end

Spare time to start a wave of TypeScript and try to connect a background management system to TypeScript. After all, only in actual combat can we know what are the shortcomings. The above records are about how to use TypeScript in Vue and the problems encountered. At present, TypeScript has not been formally used in the work. Learning new technology needs cost and time. Most of them are respected by some medium and large companies. In a word, learning more is always a good thing. We should see more and practice more when we study. The more we know, the more open our thinking will be and the more we can solve problems.

Reference material

Keywords: Javascript Vue TypeScript npm

Added by mikkex on Sat, 14 Dec 2019 22:12:28 +0200