preface

Recently the work has been very busy, the recovery cycle has been elongated, but still can adhere to the weekly recovery. Today I’m going to take a look at typescript in front end projects. I think it’s pretty clear why you should learn typescript. Most of the internals of the mainstream vue and React frameworks and related ecosystems are built in typescript. The reason for this is that its static type checking greatly improves the readability and maintainability of code, and it is very convenient to locate problems. Here’s the official typescript definition for you to understand:

TypeScript is a free and open source programming language developed by Microsoft that is a superset of JavaScript and supports the ECMAScript 6 standard. Its design goal is to develop large applications that can be compiled into pure JavaScript that can run on any browser.

This article will introduce you to typescript by introducing the core ts knowledge and practical examples.

The profile

Learning any language requires a system of learning and thinking, and the front end is no exception. The author will explain it according to the structure shown in the following figure:

The body of the

At present, webpack is most used in our project development. For TS, it is also convenient for us to compile and configure it through TS-Loader. In order to reduce the difficulty of learning TS, the author recommends using Vue-CLI3 or UMI to directly build TS projects, so as to get started with TS development faster.

Key knowledge points

1. Basic types

TypeScript supports almost the same data types as JavaScript, and provides useful enumeration types for easy use. Here’s a brief introduction to these types of usage.

// Boolean type
let isCookie:boolean = true

// Value type
let myMoney:number = 12

// The value is a string
let name:string = 'Xu Xiaoxi'

// There are two ways to represent an array type. The first way is to add [] to the element type to represent an array of elements of that type
let arr:number[] = [1.2.2]

// Array type, use array generics
let arr:Array<number> = [1.2.2]

// The tuple type is allowed to represent an array with a known number and type of elements. The elements need not be of the same type
let xi: [string.number];
// Initialize xi
xi = ['xu'.10]; / / right
xi = [11.'xu']; / / error

// Enumeration type, which can give a friendly name to a set of values
enum ActionType { doing, done, fail }
let action:ActionType = ActionType.done    / / 1

// any, indicating any type, can bypass the type checker to check these values
let color:any = 1
color = 'red'

When a function does not return a value, it is usually set to return a value of type void
function getName() :void {
    console.log("This is my name");
}

// The object type represents a non-primitive type, that is, a type other than number, string, Boolean, symbol, null, or undefined
let a:object;
a = {props: 1}
Copy the code

These are the types commonly used in typescript, and they are the basics we need to know. It’s worth adding that typescript’s type assertions are also useful for resolving TS warnings. For example, if we know exactly what type a data is, we can do this:

let arrLen: number = (someValue as Array<string>).length;
// Troubleshoot the error in setting properties on Windows
(window as any).name = 'xuxi'
Copy the code

2. The interface

One of TypeScript’s core principles is type checking of the structure that values have. In TypeScript, interfaces are used to name these types and define contracts for your code or third-party code. Let’s look at how to define and use interfaces:

interface Product {
  name: string;
  size: number;
  weight: number;
}

let product1:Product = {
	name: 'machine1'.size: 20.weight: 10.5
}
Copy the code

The type checker does not check the order of the properties, as long as the corresponding properties exist and the type is correct. Second, we can define optional and read-only attributes. Optional properties indicate that certain properties in the interface are not required and can be defined or not. Readable properties cause certain properties in an interface to be read but not assigned. Specific cases are as follows:

interface Product {
  name: string; size? :number;
  readonly weight: number;
}
Copy the code

In actual scenarios, we often encounter uncertain attribute name and attribute value type, which usually occurs in the third SDK access or the back-end response. At this time, we can use index signature to set additional attributes and types. The case is as follows:

interface App {
  name: string; color? :number;
  [propName: string] :any;
}
Copy the code

In addition to describing normal objects with properties, interfaces can also describe function types. We need to define a call signature for the interface, and each parameter in the parameter list needs a name and type. Here are some examples:

interface MyFunc {
  (value:string.type: number) :boolean;
}
/ / use
let myLoveFront: MyFunc;
myLoveFront = function(value:string.type: number) {
 	return type > 1
}
Copy the code

In vue and React development, we often use classes to write reusable components and libraries. Since TS can describe function types, can it also be used to describe class types? The answer is yes, but the definition of a class interface is a little more complicated, as we all know that classes have two types: the type of the static part and the type of the instance. When a class implements an interface, type checking is performed only on the examples. Constructor exists in the static part of the class, so it is not within the scope of the review. This is a very important statement, and we should also focus on this feature when defining class interfaces. Here is an example:

interface TickConstructor {
    new (hour: number.minute: number): TickInterface;
}
interface TickInterface {
    tick();
}

function createClock(ctor: ClockConstructor, hour: number, minute: number) :TickInterface {
    return new ctor(hour, minute);
}

class DigitalClock implements TickInterface {
    constructor(h: number, m: number){}tick() {
        console.log("xu xu"); }}class MyTick implements TickInterface {
    constructor(h: number, m: number){}tick() {
        console.log("tick tock"); }}let digital = createClock(DigitalTick, 12.17);
let analog = createClock(MyTick, 7.32);
Copy the code

After mastering these key interface types and usage methods, the study of TS can be basically started.

Class 3.

We’ve covered the topic of class interfaces above, so let’s take a closer look at classes. As with JS classes, typescript classes have public, private, and protected modifiers. The specific meaning is as follows:

  • Public In TypeScript, members are public by default. We have free access to the members defined in the program
  • Private When a member is marked private, it cannot be accessed outside the class that declared it
  • Protected is similar to private, but protected members are still accessible in derived classes

Specific cases are as follows:

class Person {
    protected name: string;
    constructor(name: string) { this.name = name; }}class Employee extends Person {
    private department: string;

    constructor(name: string, department: string) {
        super(name)
        this.department = department;
    }

    public getElevatorPitch() {
        return `Hello, my name is The ${this.name} and I work in The ${this.department}. `; }}Copy the code

We can also define a readonly modifier and a static property for a property in a class. The only thing worth saying is abstract class.

Abstract classes are used as base classes for other derived classes. They are not usually instantiated directly. Unlike interfaces, abstract classes can contain implementation details of members. The abstract keyword is used to define an abstract class and to define abstract methods within an abstract class.

Examples of abstract classes are as follows:

abstract class MyAbstract {
    constructor(public name: string) {}
    say(): void {
        console.log('say name: ' + this.name);
    }
    abstract sayBye(): void; // Must be implemented in a derived class
}

class AccountingMyAbstract extends MyAbstract {
    constructor() {
        super('小徐'); // Super () must be called in the constructor of a derived class
    }
    sayBye(): void {
        console.log('Interesting evening.');
    }
    getOther(): void {
        console.log('loading... '); }}let department: MyAbstract; // Allow to create a reference to an abstract type
department = new MyAbstract(); // Error: cannot create an instance of an abstract class
department = new AccountingMyAbstract(); // Allow instantiation and assignment of an abstract subclass
department.say();
department.sayBye();
department.getOther(); // Error: method does not exist in declared abstract class
Copy the code

4. The function

Function types have already been covered, but here’s the concept of optional arguments. Every parameter in JavaScript is optional and can be passed or not. The value is undefined when no argument is passed. In TypeScript we can use it next to the parameter name, right? Implement the function of optional parameters. Specific cases are as follows:

function createName(firstName: string, lastName? :string) {
    if (lastName)
        return firstName + "" + lastName;
    else
        return firstName;
}
Copy the code

Note that our optional arguments must be followed by the mandatory arguments.

5. The generic

We can use generics to create reusable components that can support multiple types of data. This allows users to use components with their own data types. Generics are a tricky part of typescript, but so important that they can be used by almost any third-party component library. Let’s start with the simplest example:

function iSay<T> (arg: T) :T {
    return arg;
}
// Call the generic function
let come = iSay<number> (123); 
Copy the code

We added a type variable T to iSay. T helps us capture the type (e.g., string) passed in by the user so that we can use it. Then we use T again as the return value type. Now we know that the parameter type is the same as the return value type. This allows us to track information about the types used in the function.

We can also use the generic variable T as part of the type, rather than the entire type, to increase our flexibility. Here’s an example:

function iSay<T> (arg: T[]) :T[] {
	console.log(arg.length)
    return arg;
}
Copy the code

Similar to the definition of a function type, we can define a generic interface, and we can take a generic parameter as a parameter to the entire interface, so we know exactly which generic type is being used. The case code is as follows:

interface SayLove {
	<T>(arg: T): T
}

// Take the generic parameter as an argument to the entire interface
interface SayLoveArg<T> {
	(arg: T): T
}

// A generic function
function iSay<T> (arg: T) :T {
    return arg;

let mySay1:SayLove = iSay
let mySay2:SayLoveArg<number> = iSay
Copy the code

In the same way we can define generic classes. We just need to enclose the generic type with (<>), following the class name. Specific cases are as follows:

class MyNumber<T> {
    year: T;
    compute: (x: T, y: T) = > T;
}

let myGenericNumber = new MyNumber<number> ();Copy the code

We can also define generic constraints to more accurately control the type of the class. Here are some examples:

interface NumberControl {
	length: number
}

class MyObject<T extends NumberControl> (arg: T) :T {
	console.log(arg.length)
    return arg
}
Copy the code

Advanced type

In typescript’s advanced types we cover the following core elements:

  • Cross type
  • The joint type
  • Polymorphic this type
  • Index type Indicates the query operator
  • Index access operator

Crossover typing is the merging of multiple types into a single type. We can add several existing types together to form one type. Here is a classic example:

function extend<T.U> (first: T, second: U) :T & U {
    letresult = <T & U>{}; for (let id in first) { (<any>result)[id] = (<any>first)[id]; } for (let id in second) { if (! result.hasOwnProperty(id)) { (<any>result)[id] = (<any>second)[id]; } } return result; }Copy the code

We represent the union with the character &, and the return value in the above code will have the types T and U.

A union type indicates that a value can be one of several types. We use a vertical bar (|) separated for each type, so the number | string | Boolean said a value can be a number, a string, or Boolean. Specific examples are as follows:

let name: string | number = 'xuxiaoxi'

function sayName(name: string) : (string|number) { 
  return name 
}
Copy the code

It is important to note that if a value is of a union type, we can only access members that are common to all types of that union type.

Another common requirement is to support chained calls to class methods after we implement our classes. In this case, we should return this. In typescript, we need to know about polymorphic this. It represents a subtype that contains a class or interface. This is called F-bounded polymorphism. To support chained in typescript, we could write:

class MyCalculator {
    public constructor(number = 0){}public add(n: number) :this {
        this.value += n;
        return this;
    }
    public multiply(n: number) :this {
        this.value *= n;
        return this;
    }
    / /... Other operations...
}

let v = new MyCalculator(2).multiply(5).add(1);
Copy the code

The next thing we need to know is the index type query operator. It’s usually keyof. For any type T, the result of keyof T is the union of known public property names on T. Let’s say we define an interface Animal:

interface Animal {
    cat: string;
    dog: string;
}

let AnimalProps: keyof Animal; // 'cat' | 'dog'
Copy the code

Keyof Animal is can completely with the ‘cat’ | ‘dog’ replace each other. Different is if we add other attributes to the Animal, such as pig: string, then keyof Animal will automatically become a ‘cat’ | ‘dog’ | ‘pig’.

7. Namespaces

The primary purpose of namespaces is to organize code so that you can record their types without worrying about name conflicts with other objects. Because the usage of namespaces is very simple, here we use the popular online D3 as an example, the code is as follows:

declare namespace D3 {
    export interface Selectors {
        select: {
            (selector: string): Selection;
            (element: EventTarget): Selection;
        };
    }

    export interface Event {
        x: number;
        y: number;
    }

    export interface Base extends Selectors {
        event: Event; }}declare var d3: D3.Base;
Copy the code

With the basics covered, let’s take a look at how to use third-party libraries that support typescript. For example, we often use lodash, then the correct steps are as follows:

// Install lodash and the corresponding type file
npm install --save lodash @types/lodash

// Use in code
import * as _ from "lodash";
_.padStart("Hello xuxiaoxi!".12."");
Copy the code

Declaration documents are also a very important point. Typescript often reports errors when using undeclared global functions or global variables, so we can add an xxx.d.ts file in the appropriate place and declare the variables we need. Ts will automatically retrieve the file and parse it. Here are a few examples for your reference:

// global.d.ts

// Declare global variables
declare var name: string;

// Global function
declare function say(name: string) :void;

// An object with attributes
declare namespace myObj {
    function say(s: string) :string;
    let money: number;
}

// A reusable interface
interface Animal {
	kind: string;
    age: number; weight? :number;
}
declare function findAnmiate(animal:Animal) :void
Copy the code

Of course, there are many more useful declarations that can be defined, but I won’t use any of them here.

React + Typescript project in action

1. Build react+typescript projects with UMI

In order to help you get started with typescript development, we use UMI to build a ts support project. If you are not familiar with it, you can refer to my previous article on UMI.

2. Define the global declaration file

We create a global.d.ts declaration file in the project SRC directory to handle global declarations and to be compatible with third-party libraries.

Let’s create a utils directory under SRC to store our utility classes or common libraries. For example, create a tool.ts directory under utils as our common utility function.

/** * Generate uUID */
const uuid = ():string= > {
    let s:Array<any> = [];
    let hexDigits:string"0123456789abcdef";
    for (let i = 0; i < 36; i++) {
        s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = "4"; 
    s[19] = hexDigits.substr((s[19] & 0x3) | 0x8.1); 
    s[8] = s[13] = s[18] = s[23] = "-";
    let uuid = s.join("");
    return uuid;
};

// Can come from an external type.ts file
interface Params {
  [propName: string] :string | number
}

/** * reverseJson Reverts the key-value pair of the object *@param {object} Obj Specifies the object to be inverted@param {object} Target Indicates the target object of the inversion
const reverseJson = (obj:Params = {}, target:Params = {}):Params= > {
  Object.keys(obj).forEach((key:string) = > { target[obj[key]] = key })
  return target
}
Copy the code

The above is just a few simple cases, is not perfect, you can achieve the corresponding packaging according to their own needs.

4. Use typescript in the React component

I’ll follow up with this chapter in the next article to give you a concrete look at actual typescript development.

The last

If you want to learn moreH5 game.webpack.node.gulp.css3.javascript.nodeJS.Canvas Data VisualizationAnd other front-end knowledge and actual combat, welcome in the public number “interesting talk front-end” to join our technology group to learn and discuss together, jointly explore the boundary of the front-end.

More recommended

  • Quickly implement SSR (server-side rendering) in your Vue/React app
  • Programmers must have several common sorting algorithm and search algorithm summary
  • Front-end advanced from zero to one to achieve unidirectional & bidirectional linked list
  • Preliminary study of micro front-end architecture and my front-end technology inventory
  • Implementing a CMS full stack project from 0 to 1 based on nodeJS (Part 1)
  • Implement a CMS full stack project from 0 to 1 based on nodeJS (middle) (source included)
  • CMS full stack project Vue and React (part 2)
  • Develop a component library based on VUE from zero to one
  • Build a front-end team component system from 0 to 1 (Advanced Advanced Prerequisite)
  • Javascript Design Patterns front-end Engineers Need to Know in 15 minutes (with detailed mind maps and source code)