preface

Actually,VueThe official fromX 2.6.Version begins with partial useTsRewritten.

Personally, I don’t have a positive view on stricter type restrictions, after all, all kinds of conversion of the style of writing habit.

One of my most recent projects is TypeScript+ Vue. … How sweet!

Note the “before” of the title of this article. This article is intended to cover the use of Ts blending frameworks, not the Class API

1. Build using official scaffolding

npm install -g @vue/cli
# OR
yarn global add @vue/cli
Copy the code

The new Vue CLI tool allows developers to create new projects using the TypeScript integrated environment.

Just run vue create my-app.

The command line then asks you to select the default. Use the arrow keys to select Manually Select Features.

Next, just make sure the TypeScript and Babel options are selected, as shown below:

When you’re done, it asks you if you want to use classstyle Component syntax.

Then configure the rest of the Settings to look like the figure below.

The Vue CLI tool will now install all dependencies and set up the project.

Anyway, start running.

2. Parse the project directory

When you look at the directory structure with the tree directive, you can see that the structure is quite different from what you would normally build.

The main focus here is on the shims-tsX.d. ts and shims-vue.d.ts files

In two sentences:

  • shims-tsx.d.tsYou are allowed to.tsxAt the end of the fileVueWrite in a projectjsxcode
  • shims-vue.d.tsIt is mainly used forTypeScriptidentify.vueFile,TsImport is not supported by defaultvueThe file, the file tellstsThe import.vueFiles are according to theVueConstructor<Vue>To deal with.

At the moment we open the kind of SRC/components/HelloWorld vue, will find that writing is different

<template>
  <div class="hello"> <h1>{{ msg }}</h1> <! </div> </template> <script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
exportdefault class HelloWorld extends Vue { @Prop() private msg! : string; } </script> <! -- Add"scoped" attribute to limit CSS to this component only -->
<style scoped></style>
Copy the code

At this point, we’re ready to open a new chapter: TypeScript Speedup and Vue-Property-Decorator

3. TypeScriptIntroduction to speed

3.1 Basic types and extension types

Typescript shares the same basic types as Javascript, but there are a few additional types.

  • tuplesTuple
  • The enumerationenum
  • AnyVoid

1. Collection of basic types

// Numbers are supported, including two, eight, and hexadecimallet decLiteral: number = 6;
lethexLiteral: number = 0xf00d; // This is a stringlet name: string = "bob";
let sentence: string = `Hello, my name is ${ name }. // Array. The second way is to use the Array generic, Array< element type > :let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];

let u: undefined = undefined;
let n: null = null;

Copy the code

2. Special types

1. tuplesTuple

Thinking of tuples as organized arrays, you need to pre-define the data types in the right order.

const messyArray = [' something'.2.true.undefined.null];
const tuple: [number, string, string] = [24."Indrek" , "Lasn"]
Copy the code

Typescript warns you if you don’t follow the indexing rules that sort tuples by default.

(The first item of a tuple should be of type number)

2. The enumerationenum

The enum type is a complement to the JavaScript standard data type. As in other languages such as C#, enumeration types are used to give friendly names to sets of values.

Enum Color {Red = 1, Green = 2, Blue = 4} enum Color {Red = 1, Green = 2, Blue = 4}let c: Color = Color.Green;

letcolorName: string = Color[2]; console.log(colorName); / / output'Green'Because in the code above it has a value of 2Copy the code

Another good example is the use of enumerations to store application state.

3. Void

In Typescript, you must define return types in functions. Like this:

If no value is returned, an error is reported:

We can define the return value as void:

There will be no return at this point

4. Any

Emmm… It just doesn’t matter what type you’re dealing with, you can use this when you’re not sure what type you’re dealing with.

But use it carefully, too much will lose the meaning of using Ts.

let person: any = "Front end troubleshooter"
person = 25
person = true
Copy the code

The application scenarios are as follows:

  1. Accessing a third-party library
  2. Ts vegetable force is used in the early stage
5. Never

To put it mildly: “Never is the father you will Never have.”

The specific actions are:

  • throw new Error(message)
  • return error("Something failed")
  • While (true) {} // There is an endpoint that cannot be reached

3. Type assertion

The shorthand definition is that it can be used to manually specify the type of a value.

There are two ways to write it, Angle brackets and as:

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;
Copy the code

Examples are:

When TypeScript does not determine what type a variable of a union type is, we can only access properties or methods that are common to all types of the union type:

function getLength(something: string | number): number {
    return something.length;
}

// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
//   Property 'length' does not exist on type 'number'.
Copy the code

An error will be reported if you access the length, and sometimes, we do need to access a property or method of one of the types before we know what type it is, so we need an assertion to avoid an error:

function getLength(something: string | number): number {
    if ((<string>something).length) {
        return (<string>something).length;
    } else {
        returnsomething.toString().length; }}Copy the code

3.2 generics:Generics

A major part of software engineering is building components that are not only clearly defined and have uniform interfaces, but are also reusable. Components that support existing data types and future additions provide great flexibility in the development process for large software systems.

In C# and Java, you can use “generics” to create reusable components that support multiple data types. This allows users to use components based on their data types.

1. Generic methods

In TypeScript, there are two ways to declare generic methods:

function gen_func1<T>(arg: T): T {
    returnarg; } / / orlet gen_func2: <T>(arg: T) => T = function (arg) {
    return arg;
}
Copy the code

There are also two methods of calling:

gen_func1<string>('Hello world');
gen_func2('Hello world'); // In the second call, the type parameter can be omitted, because the compiler will automatically identify the corresponding type based on the parameters passed.Copy the code

2. The generic andAny

The “Any” special type of Ts can be used in place of Any type. At first sight, there is no difference between the two types.

// Method 1: the method with any parameterfunction any_func(arg: any): any {
    console.log(arg.length);
		returnarg; } // Array generic methodfunction array_func<T>(arg: Array<T>): Array<T> {
	  console.log(arg.length);
		return arg;
}
Copy the code
  • Method number one, it’s printedargParameters of thelengthProperties. becauseanyCan be substituted for any type, so the method is passed either an array or withlengthProperty object, an exception is thrown.
  • Method two defines the parameter type isArrayFor generic types, there must belengthProperty, so no exception will be thrown.

3. Generic types

Generic interfaces:

interface Generics_interface<T> {
    (arg: T): T;
}
 
function func_demo<T>(arg: T): T {
    return arg;
}

letfunc1: Generics_interface<number> = func_demo; func1(123); // The actual parameter func1 of the correct type ('123'); // The actual parameter of the error typeCopy the code

3.3 User-defined Types:Interface vs Type alias

Interface.

Type alias.

The following is from:

What’s the difference between interface and type in Typescript

1. The similarities

Can be used to describe an object or function:

interface User {
  name: string
  age: number
}

type User = {
  name: string
  age: number
};

interface SetUser {
  (name: string, age: number): void;
}
type SetUser = (name: string, age: number): void;

Copy the code

Both allow an extension (extends) :

Both interface and Type can be extended, and they are not independent of each other, which means interface extends Type and type extends Interface. The effect is similar, but the syntax is different.

interface extends interface

interface Name { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
Copy the code

type extends type

type Name = { 
  name: string; 
}
type User = Name & { age: number  };
Copy the code

interface extends type

type Name = { 
  name: string; 
}
interface User extends Name { 
  age: number; 
}
Copy the code

type extends interface

interface Name { 
  name: string; 
}
type User = Name & { 
  age: number; 
}

Copy the code

2. The difference between

typeCan theinterfaceno

  • typeYou can declare basic type aliases, union types, tuples, and so on
// Basic type aliasestypeName = string // union type interface Dog {wong(); } interface Cat { miao(); }typePet Dog = | Cat / / specific definition array type of each positiontype PetList = [Dog, Pet]
Copy the code
  • typeStatement can also be usedtypeofGets the type of the instance for assignment
// use typeof when you want to get the typeof a variablelet div = document.createElement('div');
type B = typeof div
Copy the code
  • Other SAO operation
type StringOrNumber = string | number;  
type Text = string | { text: string };  
type NameLookup = Dictionary<string, Person>;  
type Callback<T> = (data: T) => void;  
type Pair<T> = [T, T];  
type Coordinates = Pair<number>;  
type Tree<T> = T | { left: Tree<T>, right: Tree<T> };
Copy the code

interfaceCan thetypeno

Interfaces can declare merges

Interface User {name: string age: number} interface User {sex: string} /* User Interface: {name: string age: number sex: string } */Copy the code

Interface has optional properties and read-only properties

  • Optional attribute

    Not all properties in an interface are required. Some exist only under certain conditions, or not at all. For example, if only some properties are assigned to the parameter object passed to a function. An interface with optional attributes is similar to a normal interface definition, except that the optional attribute name definition is followed by a? Symbols. As shown below.

interface Person { name: string; age? : number; gender? : number; }Copy the code
  • Read-only property

    As the name implies, this property is not writable, and the object property can only change its value when the object is newly created. You can specify a read-only property by using readonly before the property name, as shown below:

interface User {
    readonly loginName: string;
    password: string;
}
Copy the code

As the above example shows, loginName cannot be changed after the User object is initialized.

3.4 Implementation and Inheritance:implementsvsextends

Extends is obviously an ES6 class inheritance, so what does implement do? How is it different from extends?

Implement. Similar to the basic use of interfaces in C# or Java, TypeScript can use them to explicitly force a class to conform to a contract

Implement basic usage:

interface IDeveloper { name: string; age? : number; } // OK class dev implements IDeveloper { name ='Alex';
    age = 20;
}
// OK
class dev2 implements IDeveloper {
    name = 'Alex';
}
// Error
class dev3 implements IDeveloper {
    name = 'Alex';
    age = '9';
}
Copy the code

Extends extends is the parent of an inheritance class, so you can use both of them in a mixed way:

 class A extends B implements C,D,E
Copy the code

With interface and type:

3.5 Declaration files and Namespaces:declarenamespace

Before we talked about shims-tsX.D. ts and Shims-vue.D. ts in project Vue, the initial content of which looks like this:

// shims-tsx.d.ts
import Vue, { VNode } from 'vue';

declare global {
  namespace JSX {
    // tslint:disable no-empty-interface
    interface Element extends VNode {}
    // tslint:disable no-empty-interface
    interface ElementClass extends Vue {}
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}

// shims-vue.d.ts
declare module '*.vue' {
  import Vue from 'vue';
  export default Vue;
}

Copy the code

Declare: When using a third-party library, we need to reference its declaration file to get code completion, interface hints, and other functions.

Here are a few common ones:

declareVar declares global variablesdeclare functionDeclaring global methodsdeclareClass Declares a global classdeclareEnum Specifies a global enumeration typedeclareGlobal Extends global variablesdeclareModule Extension moduleCopy the code

Namespace: “internal module” is now called “namespace”

Module X {equivalent to the now recommended namespace X {)

Collaborate with other JS libraries

It is also possible to create a.d.ts file declaration file for other JS libraries that use the namespace. For example, for D3 JS libraries, you can create such a declaration file:

declare namespace D3{
    exportinterface Selectors { ... }}declare var d3: D3.Base;
Copy the code

So the above two files:

  • shims-tsx.d.ts, in the global variableglobalSeveral internal modules are batch named in
  • shims-vue.d.ts“Means to tellTypeScript *.vueSuffixed files can be handed invueModule to handle.

3.6 Access modifier:private,public,protected

It makes sense:

  1. The default for the public

  2. When a member is marked private, it cannot be accessed outside the class that declared it. For example:

class Animal { private name: string; constructor(theName: string) { this.name = theName; }}let a = new Animal('Cat').name; // Error, 'name' is privateCopy the code
  1. protectedandprivateSimilar, but,protectedMembers are accessible in derived classes
class Animal {
    protected name: string;
    constructor(theName: string) {
        this.name = theName;
    }
}

class Rhino extends Animal {
    constructor() {
        super('Rhino');
   }         
   getName() {console.log(this.name)}}Copy the code

3.7 Optional parameters (? 🙂 and the non-empty assertion operators (! .).

Optional parameters

functionbuildName(firstName: string, lastName? : string) {return firstName + ' '+ lastName} // Error demo buildName("firstName"."lastName"."lastName") // Demonstrate buildName correctly ("firstName") // Demonstrate buildName correctly ("firstName"."lastName")
Copy the code

Non-empty assertion operators:

Used when you can be sure that the variable value is not null.

Unlike optional arguments, non-empty assertion operators do not prevent null or undefined.

lets = e! .name; // Assert e is non-null and access the name attributeCopy the code

4. VueThe component’sTswriting

Vue has better support for TS since Vue 2.5. According to the official documentation, vue can be written in typescript in two ways:

**Vue.extend **

  import Vue from 'vue'

  const Component = Vue.extend({
  	// type inference enabled
  })
Copy the code

vue-class-component

import { Component, Vue, Prop } from 'vue-property-decorator'

@Component
export default class Test extends Vue {
  @Prop({ type: Object })
  private test: { value: string }
}
Copy the code

Ideally, vue. extend is the cheapest way to learn. On the basis of the existing writing method, almost zero cost of migration.

The vue. extend pattern, however, needs to be used in conjunction with mixins. Methods defined in mixins are not recognized by typescript

This means missing code hints, type checking, compile errors, and so on.

The rookies choose. The big boys choose the best. Let’s move on to the second one:

4.1 vue-class-component

We went back to SRC/components/HelloWorld. Vue

<template>
  <div class="hello"> <h1>{{ msg }}</h1> <! </div> </template> <script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';

@Component
exportdefault class HelloWorld extends Vue { @Prop() private msg! : string; } </script> <! -- Add"scoped" attribute to limit CSS to this component only -->
<style scoped></style>
Copy the code

For those of you who have written Python, this sounds familiar:

  • vue-property-decoratorThis officially supported library provides function decorators (modifiers) syntax

1. Function modifiers@

“@” is not so much a modifier as a reference to or call to the function it decorates.

Or in plain English: @: “I’m surrounded by the following.”

For example, the following code contains two functions that have not been called, but will produce output:

test(f){ console.log("before ..." ); f() console.log("after ..." ); } @test func(){ console.log("func was called"); }Copy the code

Run directly, output results:

before ...
func was called
after ...
Copy the code

The above code can be seen:

  • Only two functions are defined:testandfuncThey are not called.
  • If there is no “@test”, the run should have no output.

However, when the interpreter reads the function modifier “@”, the following steps look like this:

  1. To call test, the entry argument to test is the function called “func”;

  2. The test function is executed, and the entry argument (i.e. Func) is called (executed);

In other words, the entry argument of the function with the modifier is the entire function below. Function a (function () {… });

2. vue-property-decoratorandvuex-classDecorator provided

Vue-property-decorator:

  • @Prop
  • @PropSync
  • @Provide
  • @Model
  • @Watch
  • @Inject
  • @Provide
  • @Emit
  • @Component (provided by vue-class-component)
  • Mixins (the helper function named mixins provided by vue-class-component)

Vuex-class decorator:

  • @State
  • @Getter
  • @Action
  • @Mutation

Let’s look at the original Vue component template:

import {componentA,componentB} from '@/components';

export default {
	components: { componentA, componentB},
	props: {
    propA: { type: Number },
    propB: { default: 'default value' },
    propC: { type: [String, Boolean]},} // Component datadata () {
    return {
      message: 'Hello'}}, // computed: {reversedMessage () {
      return this.message.split(' ').reverse().join(' '} // Vuex datastep() {
    	return this.$store.state.count
    }
  },
  methods: {
    changeMessage () {
      this.message = "Good bye"
    },
    getName() {
    	let name = this.$store.getters['person/name']
    	returnName}}, // Life cyclecreated() {},mounted() {},updated() {},destroyed() {}}Copy the code

}}}}}}}}}}}}}}

import { Component, Vue, Prop } from 'vue-property-decorator';
import { State, Getter } from 'vuex-class';
import { count, name } from '@/person'
import { componentA, componentB } from '@/components';

@Component({
    components:{ componentA, componentB},
})
export default class HelloWorld extends Vue{
	@Prop(Number) readonlypropA! : number | undefined @Prop({ default:'default value' }) readonlypropB! : string @Prop([String, Boolean])readonlypropC! : string | Boolean | undefined / / the original data message ='Hello'Private get reversedMessage (): string[] {return this.message.split(' ').reverse().join(' '} @state ((State: IRootState) => state.booking.currentstep) step! : number @Getter('person/name') name! : name // method public changeMessage (): void { this.message ='Good bye'
  },
  public getName(): string {
    let storeName = name
    returnStoreName} private created () : void {}, private Mounted () : void {}, private updated () : Void {}, private destroyed () : void {}}Copy the code

As you can see, we added the private XXXX method to the lifecycle list as this should not be exposed to other components.

The reason for not making method private is that @Emit might be used to pass messages to the parent component.

4.2 Adding global Tools

To import global modules, change main.ts:

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';

Vue.config.productionTip = false;

new Vue({
  router,
  store,
  render: (h) => h(App),
}).$mount('#app');
Copy the code

npm i VueI18n

import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store'; // New module import i18n from'./i18n';

Vue.config.productionTip = false; New Vue({router, store, i18n, // new module render: (h) => h(App),}).$mount('#app');
Copy the code

But that alone is not enough. You need to move SRC /vue-shim.d.ts:

// Declare global methodsdeclare module 'vue/types/vue' {
  interface Vue {
        readonly $i18n: VueI18Next;
        $t: TranslationFunction; }}Copy the code

If you use this.$i18n(), no error will be reported.

4.3 Axios usage and encapsulation

Axios encapsulates thousands of faces

If you want to simply experience using Axios in Ts, you can install Vue-Axios and use Axios simply

$ npm i axios vue-axios
Copy the code

The main. Ts to add:

import Vue from 'vue'
import axios from 'axios'
import VueAxios from 'vue-axios'

Vue.use(VueAxios, axios)
Copy the code

Then use it within a component:

Vue.axios.get(api).then((response) => {
  console.log(response.data)
})

this.axios.get(api).then((response) => {
  console.log(response.data)
})

this.$http.get(api).then((response) => {
  console.log(response.data)
})
Copy the code

1. Create a filerequest.ts

File directory:

-api -main. ts // Actual call -utils-request. ts // Interface encapsulationCopy the code

2. request.tsFile parsing

import * as axios from 'axios';
import store from '@/store'; // Replace import {Toast} from depending on the UI component library used'vant';
import { AxiosResponse, AxiosRequestConfig } from 'axios'; Const baseURL = process.env.vue_app_URL; /* const baseURL = process.env.vue_app_URL; /* Create axios instance */ const service = axios.default.create({baseURL, timeout: 0, // maxContentLength: 4000,}); service.interceptors.request.use((config: AxiosRequestConfig) => {return config;
}, (error: any) => {
    Promise.reject(error);
});

service.interceptors.response.use(
    (response: AxiosResponse) => {
        if(response.status ! == 200) { Toast.fail('Request error! ');
        } else {
            return response.data;
        }
    },
    (error: any) => {
        return Promise.reject(error);
    });
    
export default service;
Copy the code

To make things easier, we also need to define a fixed set of formats that Axios returns by creating ajax.ts:

export interface AjaxResponse {
    code: number;
    data: any;
    message: string;
}
Copy the code

3. main.tsInterface call:

// api/main.ts
import request from '.. /utils/request';

// get
export function getSomeThings(params:any) {
    return request({
        url: '/api/getSomethings'}); } // postexport function postSomeThings(params:any) {
    return request({
        url: '/api/postSomethings',
        methods: 'post',
        data: params
    });
}
Copy the code

5. Write a component

In order to reduce the time, we can replace the SRC/components/HelloWorld vue, do a blog post components:

<template>
	<div class="blogpost">
		<h2>{{ post.title }}</h2>
		<p>{{ post.body }}</p>
		<p class="meta">Written by {{ post.author }} on {{ date }}</p>
	</div>
</template>

<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator'; // This is where the data is typedexport interface Post {
	title: string;
	body: string;
	author: string;
	datePosted: Date;
}

@Component
exportdefault class HelloWorld extends Vue { @Prop() private post! : Post; getdate() {
		return `${this.post.datePosted.getDate()}/${this.post.datePosted.getMonth()}/${this.post.datePosted.getFullYear()}`;
	}
}
</script>

<style scoped>
h2 {
  text-decoration: underline;
}
p.meta {
  font-style: italic;
}
</style>
Copy the code

Then use it in home.vue:

<template>
  <div class="home">
    <img alt="Vue logo" src=".. /assets/logo.png">
   	<HelloWorld v-for="blogPost in blogPosts" :post="blogPost" :key="blogPost.title" />
  </div>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import HelloWorld, { Post } from '@/components/HelloWorld.vue'; // @ is an alias to /src

@Component({
  components: {
    HelloWorld,
  },
})
export default class Home extends Vue {
    private blogPosts: Post[] = [
        {
          title: 'My first blogpost ever! ',
          body: 'Lorem ipsum dolor sit amet.',
          author: 'Elke',
          datePosted: new Date(2019, 1, 18),
        },
        {
          title: 'Look I am blogging! ',
          body: 'Hurray for me, this is my second post! ',
          author: 'Elke',
          datePosted: new Date(2019, 1, 19),
        },
        {
          title: 'Another one? ! ',
          body: 'Another one! ',
          author: 'Elke',
          datePosted: new Date(2019, 1, 20),
        },
      ];
}
</script>

Copy the code

Run the project:

This is a simple parent-child component

6. Refer to articles

TypeScript — JavaScript with superpowers — Part II

VUE WITH TYPESCRIPT

TypeScript + for large projects

Python modifier (1) — function modifier “@”

What’s the difference between interface and type in Typescript

7. To summarize

Class API
class
Vue
Ts
Class API

❤️ see three things

If you found this post inspiring, I’d like to invite you to do three small favors for me:

  1. Like, let more people can also see this content (collection does not like, is playing rogue – -)
  2. Pay attention to the public number “front-end dispeller”, irregularly share original knowledge.
  3. Check out other articles as well
  • Design Patterns you Inadvertently Use (part 1) – Creation patterns
  • “King of data visualization library” D3.js fast start to Vue application
  • “True ® Path to Full Stack” a back-end guide to Web front-end development
  • “Vue Practice” 5 minutes for a Vue CLI plug-in
  • “Vue practices” arm your front-end projects
  • “Advanced front end interview” JavaScript handwriting unbeatable secrets
  • “Learn from source code” answers to Vue questions that interviewers don’t know
  • “Learn from the source code” Vue source code in JS SAO operation
  • “Vue Practice” project to upgrade vue-CLI3 correct posture
  • Why do you never understand JavaScript scope chains?

You can also go to my GitHub blog and get the source files for all the posts:

Front end exit guide: github.com/roger-hiro/…