preface

As we all know, Vue 3.0 uses TypeScript to make the already popular TypeScript even more popular. TypeScript is a free and open source programming language written by Microsoft. TypeScript has been around since October 2012 and was officially released in June 2013. TypeScript is a superset of JavaScript that extends JavaScript’s syntax to include optional static typing and class-based object-oriented programming.

A common error in JavaScript development is that a variable or attribute does not exist. However, these are low-level errors, and static type checking can remedy this shortcoming. What is static typing? Here’s an example:

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

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

As you can see, TypeScript adds a type to a variable when declaring it, and throws an error if the value and type of the variable are inconsistent. Static typing is checked only at compile time, and the resulting code is still JavaScript. Even if we assign a string variable to another type, the code will work fine.

Second, TypeScript makes code more readable and maintainable, and the type definition is actually a good document. For example, when calling a function, you can look at the type definition of the parameters and return values to get a general idea of how the function will be used.

The preparatory work

npm

Install the typescript

npm install typescript @vue/cli-plugin-typescript -D
Copy the code

The 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;
}
Copy the code
  • shims-tsx.d.ts
import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    type Element = VNode
    type ElementClass = Vue
    interface IntrinsicElements {
      [elem: string] :any; }}}Copy the code
  • 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"]}Copy the code

ESLint configuration

Why use ESLint and not TSLint?

In January, the TypeScript official release blog recommended using ESLint instead of TSLint. The ESLint team will no longer maintain typescript-esLint-Parser and will not publish it on Npm. Anyone using Tyescript-esLint-Parser should switch to @tyescript-esLint /parser.

The official explanation:

We’ve noticed that there are some architectural issues with the way TSLint rules operate that affect performance, and ESLint already has the higher-performance architecture we want from Linter. In addition, different user communities often have Lint rules built for ESLint rather than TSLint (such as React Hook or Vue rules). For this reason, our editing team will focus on leveraging ESLint rather than copying it. For scenarios that ESLint does not currently cover (such as semantic linting or program-wide linting), we will focus on equating ESLint’s TypeScript support with TSLint’s.

The original

How to use

AlloyTeam provides a comprehensive set of EsLint configuration specifications for React/Vue/Typescript projects, from which you can customize rules. GitHub

The installation

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

View the AlloyTeam ESLint rule

configuration

Create.eslintrc.js in the root directory of your project and copy the following into 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'}};Copy the code

supplement

To learn more about how to use configuration items, go to the ESLint website and search for configuration items.

If you are using VScode, it is recommended to use the ESLint plugin to aid development.

The file modification

Entrance to the file

  1. The main. Js to main. Ts
  2. Vue. Config. js modifies the entry file
const path = require('path')
module.exports = {
  ...
  pages: {
    index: {
      entry: path.resolve(__dirname+'/src/main.ts')}},... }Copy the code

Vue component files

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

Vue also provides a TypeScript decorator for class-style components, which should be set to true in tsconfig.json before using it.

Install the VUE decorator

The vue-property-decorator library relies entirely on vue-class-Component and is installed together at installation time

npm install vue-class-component vue-property-decorator -D
Copy the code

Transformation. Vue

Only the things in srCIPT need to be modified, nothing else needs to be changed

<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>
Copy the code

Class member modifier, which defaults to public if no modifier is added

  • Public: public. Members of a class can be freely accessed
  • Protected: The class and its inherited subclasses are accessible
  • Private: Private, accessible only by classes

Prop

! : Use explicit assignment assertion modifiers for attributes. See documentation for more

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

Is equivalent to

export default{... props:{id: {type: Number.default: 0}}... }Copy the code

Watch

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

Is equivalent to

export default{... watch:{ dialogFormVisible(newVal){// Some operations}}... }Copy the code

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 }Copy the code

Is equivalent to

// App.vue
export default {
  provide() {
    return {
      'app': this}}}// MyComponent.vue
export default {
  inject: ['app']}Copy the code

For more decorators to use, refer to the vue-property-decorator documentation

Global declarations

*. Which s file

As a superset of JavaScript, TypeScript provides type definition files (*.d.ts) to support the type definition of these libraries. Developers write type definition files and publish them to NPM. When users need to use the library in a TypeScript project, they can download the package separately so that the JS library can run in a TypeScript project.

For example, MD5 is believed to be used by many people. This library can convert a string into a string of hash values, which is irreversible. It is often used for hashing sensitive information and then sending it to the back end for verification to ensure data security. If we want to use it in TypeScript projects, we also need to download @tyeps/md5 separately, where we can see the types defined for MD5 in the index.d.ts folder.

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

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

declare namespace md5 {}

export = md5;
Copy the code

How does TypeScript recognize *.d.ts

TypeScript automatically recognizes *.d.ts files globally when a project is compiled. All we need to do is write *.d.ts, and TypeScript will inject these written type definitions into the global provision.

Add attributes/methods to the VUE instance

When we use this.$route or some prototypical methods, typescript can’t infer that the $route attribute doesn’t exist at compile time. We need to add global declarations for these global attributes or methods

Make changes to shims-vue.d.ts, of course you can also choose to customize *.d.ts to add declarations

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; }}Copy the code

Custom type definition file

When some types or interfaces need to be used frequently, we can write global type definitions for the project. The @types folder is created under the root path and contains *.d.ts files, which are 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 files. Declare can only scope the outermost layer
/ / 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;
}
Copy the code

Free hands, transvue2ts conversion tool

The most troublesome part of the transformation process is the grammar transformation. The content is some fixed writing methods. These repetitive and boring tasks can be handed over to the machine. We can improve our efficiency here with the Transvue2ts tool, which converts data, prop, watch, and so on into decorator syntax.

The installation

npm i transvue2ts -g
Copy the code

use

After installation, the transvue2TS library path will be written to the system path, which can be used directly by opening the command line tool. The second parameter of the command is the full path of the file. Executing the command will generate a new converted file in the same directory, such as index.vue in the View folder, which will generate indexts.vue.

Handle single-file components

Transvue2ts D:\typescript-vue-admin-demo\ SRC \pages\index.vue => Output path: D:\typescript-vue-admin-demo\ SRC \pages\ indexts.vueCopy the code

Process all vUE component files under the folder

Transvue2ts D:\typescript-vue-admin-demo\ SRC \pages => output path: D:\typescript-vue-admin-demo\ SRC \pagesTSCopy the code

supplement

Don’t think that having a tool really frees your hands completely, tools just help us to transform part of the grammar. Syntax and parameter type definitions that the tool fails to handle still need to be fixed. Note that comments are filtered out after the conversion.

The tool author introduces the tool in digging gold

About third-party library use

Some third-party libraries include type definition files when they are installed, so you can use the official type definitions without having to define them yourself.

Find the package folder in node_modules. Type files are usually stored in the types folder. In fact, type definition files are like documents, which clearly show the required parameters and parameter types.

Here are some examples of using tripartite libraries in Vue:

Element-ui component parameter

Using type Definitions

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(); }); }... }Copy the code

Elder-ui /types/loading. There are many comments in the original file describing each property

export interfaceLoadingServiceOptions { target? : HTMLElement |stringbody? :booleanfullscreen? :booleanlock? :booleantext? :stringspinner? :stringbackground? :stringcustomClass? :string
}
export declare class ElLoadingComponent extends Vue {
  close (): void
}
declare module 'vue/types/vue' {
  interface Vue {
    $loading (options: LoadingServiceOptions): ElLoadingComponent
  }
}
Copy the code

Vue-router Hook function

Using type Definitions

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(); }}Copy the code

In vue-router/types/router.d.ts, you can see the type definition of the 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
Copy the code

As well as the Router and Route used previously, all methods, attributes 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; }Copy the code

Custom tripartite library declarations

When using a tripartite library without a *.d.ts declaration file, the project will compile with an error like this:

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'; `Copy the code

Vuedraggable could not find the declaration file, try installing @types/vuedraggable(if present), or customize a new declaration file.

Install @ types/vuedraggable

Install @types/vuedraggable and 404 not found, indicating that the package does not exist. It feels like a lot of people are using it (18 million downloads per week), but the community doesn’t have a declaration file.

Custom declaration file

I had no choice but to choose the second way. To be honest, I also fumbled for a little time (mainly I didn’t know much about this aspect, not familiar with it).

Start by creating a vuedraggable folder under node_modules/@types, or create your own @types folder if you don’t have one. Create index.d.ts under vueDraggable. Write the following:

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

No error is reported after recompilation, and the problem is resolved.

Suggestions and Precautions

Transformation process

  • When you plug in TypeScript, you don’t have to change all of your files to TS syntax at once. The original syntax works fine, and it’s best to change it individually
  • It is normal to have a large number of errors during the initial transformation, which are basically type errors. Modify corresponding errors according to the error prompts
  • When importing ts files, you do not need to add.tsThe suffix
  • Global variables defined for the project do not work properly, rerun the server (I have encountered it…)

Have a problem

  • Search engine oriented, if you know what the problem is, right
  • Read the documentation carefully. Most of the errors are basic and the documentation will fix them
  • Github look for TypeScript projects to see how others are writing

Write in the last

Spend some of your spare time getting started with TypeScript and trying to integrate a back-end management system into TypeScript. After all, you have to be practical to know the drawbacks. This is how to use TypeScript in Vue and the problems encountered. Currently, TypeScript is not used formally in the workplace. Learning new technologies takes time and cost, and most of them are favored by large and mid-sized companies. In a word, learning more is always a good thing, learn to see more practice, know more thinking will be more open, the more ideas to solve problems.

The resources

  • TypeScript tutorial
  • Technical Fat -TypeScript free video tutorial