Author: Become a good little white

Making: making

Hobby: Americano More Ice!

An inner volume note about Typescript

  • Typescript
    • Introduction to the
    • The installation
    • Write your first demo
    • basis
      • Primitive data type
        • Boolean value
        • The numerical
        • string
        • A null value
      • Any value
      • Type inference
      • The joint type
      • Object type – Interface
      • An array type
      • Function types
      • Type Assertion
        • Type assertion purpose
        • Restrictions on type assertions
        • Double assertion
        • Type assertion vs type conversion
        • Type assertion vs type declaration
        • Type assertion vs generics
      • Declaration file
        • New syntactic index
        • What is a declaration statement
        • What is a declaration file
          • Third Party Declaration Document
        • Written declaration document
          • The global variable
            • declare var
            • declare function
            • declare class
            • declare enum
            • declare namespace
            • Interface and type
            • Preventing naming conflicts
            • A statement to merge
          • NPM package
            • export
            • Mix DECLARE and export
            • export namespace
            • export default
            • export =
        • UMD library
          • export as namespace
        • Expand global variables directly
        • Extend global variables in NPM packages or UMD libraries
          • declare global
        • Module plug-ins
        • Declare dependencies in files
          • Three slash instruction
            • Write a global variable declaration file
            • A declaration file that depends on a global variable
            • Split declaration file
        • Automatically generate declaration files
        • Release statement file
      • Built-in objects
        • ECMAScript built-in object
        • DOM and BOM built-in objects
        • The TypeScript core library definition file
        • Written in TypeScript Node. Js
    • The advanced
      • Type the alias
      • String literal type
      • tuples
      • The enumeration
        • Manual assignment
        • Constant term and computed term
        • Constant enumeration
        • External enumeration (Ambient Enums)
      • class
        • The concept of class
        • ES6 class usage
          • Properties and methods
          • Class inheritance
          • accessor
          • A static method
        • ES7 class usage
          • Instance attributes
          • Static attributes
        • TypeScript class usage
          • Public, private and protected
          • Parameter properties
          • readonly
          • An abstract class
        • The type of the class
      • Classes and interfaces
        • Class implementation interface
        • Interface Inheritance interface
        • Interface inheritance class
      • Generics
        • Simple example
        • Multiple type parameters
        • Generic constraint
        • A generic interface
        • A generic class
        • The default type of a generic parameter
      • A statement to merge
        • Combination of functions
        • Merging interfaces
        • Such merger
    • engineering
      • Code review
        • What is code review
        • Why code review
        • Use ESLint in TypeScript
          • Install ESLint in the project
          • Creating a Configuration File
          • Check a TS file
          • Review the TS files for the entire project
          • Integrate ESLint checking with VSCode
          • Use Prettier to fix formatting errors
          • ESLint configuration using AlloyTeam
          • Use ESLint to check TSX files

Introduction to the

TypeScript is a superset of JavaScript types that can be compiled into pure JavaScript. The compiled JavaScript can run on any browser. TypeScript compilation tools can run on any server and on any system. TypeScript is open source.

The installation

npm install -g typescript

There is a TSC global command when the installation is complete

Write your first demo

// hello.ts
function sayHello(person: string) {
    return 'Hello, ' + person;
}

let user = 'Tom';
console.log(sayHello(user));
Copy the code

Execute TSC hello.ts to generate hello.js

function sayHello(person) {
    return 'Hello, ' + person;
}
var user = 'Tom';
console.log(sayHello(user));
Copy the code

Compare the differences:

  • tsInsert parameters will have parameter types,tsOnly static checks are performed, and if any errors are found, an error is reported at compile time
  • letES6The keyword, andvarSimilarly, used to define a local variable

basis

Primitive data type

JavaScript comes in two types: Primitive data types and Object types.

Primitive data types include: Boolean, numeric, string, NULL, undefined, and the new type Symbol in ES6.

Boolean value

// Boolean is a basic type in JavaScript, and Boolean is a constructor in JavaScript
let isDone: boolean = false;
// Returns a Boolean object
let createdByNewBoolean: Boolean = new Boolean(1);
Return a Boolean type
let createdByBoolean: boolean = Boolean(1);
Copy the code

The numerical

// Where 0b1010 and 0o744 are binary and octal representations in ES6, which are compiled to decimal numbers
let decLiteral: number = 6;
let hexLiternal: number = 0xf00d;
// Binary representation in ES6
let binaryLiteral: number = 0b1010;
// Octal notation in ES6
let octalLiteral: number = 0o744;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;
Copy the code

string

// 'is used to define template strings, and ${expr} is used to embed expressions
let myName: string = 'Tom';
let myAge: number = 25;
let sentence: string = `Hello, my name is ${myName}.I'll be ${myAge + 1} years old next day`;

Copy the code

A null value

// Indicates that no value is returned
function alertName() :void {
    alert('My name is Tom');
}
// It is useless to declare a void variable because you can only assign it to undefined and null
let unusable: void = undefined;
Copy the code
/ / Null, and Undefined
let u: undefined = undefined;
let n: null = null;

// Undefined and null are subtypes of all types
// variables of type undefined can be assigned to variables of type number
let num: number = undefined;
/ / or
let u: undefined;
let num: number = u;

// void does not work
let u: void;
let num: number = u;
// Type 'void' is not assignable to type 'number'.
Copy the code

Any value

An arbitrary value (Any) is used to indicate that assignments of Any type are allowed

After declaring a variable as an arbitrary value, any operation on it returns the type of an arbitrary value

/ / usage
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
// Access any attributes
let anyThing: any = 'hello';
console.log(anyThing.myName);
console.log(anyThing.myName.firstName);
// Call any method
let anyThing: any = 'Tom';
anyThing.setName('Jerry');
anyThing.setName('Jerry').sayHello();
anyThing.myName.setFirstName('Cat');
Copy the code

A variable is recognized as any value type if its type is not specified when it is declared

// let something: any;
let something;
something = 'seven';
something = 7;
something.setName('Tom');
Copy the code

Type inference

TypeScript extrapolates a type when no type is explicitly specified. This is called type inference

// No type is specified, but a string is assigned
// Compile error
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;

// There is no assignment at the time of the definition, and any subsequent assignment or not will be inferred to be of type any
// Not checked at all
let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
Copy the code

The joint type

Union Types indicate that the value can be one of many Types

/ / joint types using | separated for each type
// Yes, the myFavoriteNumber type is allowed to be string or number, but not other types
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
/ / an error
let myFavoriteNumber: string | number;
myFavoriteNumber = true;
Copy the code

When TypeScript doesn’t know 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

// Length is not a common attribute of string and number, so an error is reported
function getLength(something: string | number) :number {
    return something.length;
}
/ / index. Ts (2, 2) : error TS2339: Property 'length' does not exist on the type 'string | number'.
// Property 'length' does not exist on type 'number'.
Copy the code

It is ok to access the common properties of string and number

function getString(something: string | number) :string {
    return something.toString();
}
Copy the code

When a variable of a union type is assigned, a type is inferred according to the rules of type inference

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
// Infer to string
console.log(myFavoriteNumber.length); / / 5
// Deduce the number
myFavoriteNumber = 7;
console.log(myFavoriteNumber.length); // Error when compiling
Copy the code

Object type – Interface

In TypeScript, we use Interfaces to define the types of objects.

  • What is an interface

Interfaces are an important concept in object-oriented languages. They are an abstraction of behavior, and that behavior needs to be implemented by classes.

Interfaces in TypeScript are a very flexible concept. In addition to abstracting some of the behavior of a class, interfaces are often used to describe the Shape of an object.

interface Person {
    name: string;
    age: number;
}
// Tom, whose type is Person, constrains that the shape of Tom must be the same as the interface Person
// That is, the number and type of attributes should be the same
let tom: Person = {
    name: 'Tom'.age: 25
};
Copy the code
  • Optional attribute

Instead of matching a shape exactly, use optional attributes:

interface Person {
    name: string; age? :number;
}
// Optional attribute age, free to exist or not exist
let tom: Person = {
    name: 'Tom'
};
Copy the code
  • Any attribute

An interface can have arbitrary attributes, which can be used as follows

interface Person {
    name: string; age? :number;
    [propName: string] :any;
}
let tom: Person = {
    name: 'Tom'.gender: 'male'
};
Copy the code

Note: Once an arbitrary attribute is defined, the type of both the determined attribute and the optional attribute must be a subset of its type:

interface Person {
    name: string; age? :number;
    [propName: string] :string;
}
// Age is not string
let tom: Person = {
    name: 'Tom'.age: 25.gender: 'male'
};

// Use the union type in any attribute to solve the above problem
interface Person {
    name: string; age? :number;
    [propName: string] :string | number;
}
let tom: Person = {
    name: 'Tom'.age: 25.gender: 'male'
};
Copy the code
  • Read-only property

Sometimes we want fields in an object to be assigned only at creation time, so we can use readonly to define read-only properties

interface Person {
    readonly id: number;
    name: string; age? :number;
    [propName: string] :any;
}
let tom: Person = {
    id: 89757.name: 'Tom'.gender: 'male'
};
// An error was reported because the property is read-only
tom.id = 9527;
Copy the code

Note that the read-only constraint exists the first time an object is assigned, not the first time a read-only property is assigned

interface Person {
    readonly id: number;
    name: string; age? :number;
    [propName: string] :any;
}
// the id is not assigned
let tom: Person = {
    name: 'Tom'.gender: 'male'
};
// The second error is reported because it is read-only
tom.id = 89757;
Copy the code

An array type

In TypeScript, array types are defined in several ways

Such as:

let fibonacci: number[] = [1.1.2.3.5];
Copy the code

No other types are allowed in an array item

let fibonacci: number[] = [1.'1'.2.3.5];
// Type 'string' is not assignable to type 'number'.
Copy the code

The parameters of some methods of arrays are also limited by the types that are agreed upon when the array is defined

let fibonacci: number[] = [1.1.2.3.5];
fibonacci.push('8');
// Argument of type '"8"' is not assignable to parameter of type 'number'.
Copy the code
  • Array Generic

Array

represents the Array

let fibonacci: Array<number> = [1.1.2.3.5];
Copy the code
  • Interface representation array
// NumberArray indicates that the value type must be numeric if the index type is numeric
interface NumberArray {
    [index: number] :number;
}
let fibonacci: NumberArray = [1.1.2.3.5];
Copy the code
  • Class Array (array-like Object)

The array-like Object class is not an Array type, such as Arguments

function sum() {
    let args: number[] = arguments;
}
// Instead of using an array, use an interface
// The value must be of a type other than a number
// It has two attributes: length and callee
function sum() {
    let args: {
        [index: number] :number;
        length: number;
        callee: Function;
    } = arguments;
}
Copy the code

In fact, common class arrays have their own interface definitions, such as IArguments, NodeList, HTMLCollection, etc

function sum() {
    let args: IArguments = arguments;
}
// IArguments are TypeScript defined types
interface IArguments {
    [index: number] :any;
    length: number;
    callee: Function;
}
Copy the code
  • Application of any to arrays

A common practice is to use any to indicate that any type is allowed in an array

let list: any[] = ['xiaobiao'.25, { website: 'http://xcatliu.com' }];
Copy the code

Function types

There are two common ways to define functions in JavaScript

  • Function Declaration
  • Function Expression
// Function Declaration
function sum(x, y) {
    return x + y;
}

// Function Expression
let mySum = function (x, y) {
    return x + y;
};
Copy the code
  • Function declaration

Ts is a little different in that it uses constraints

function sum(x: number, y: number) :number {
    return x + y;
}
// Entering extra (or less than required) parameters is not allowed
/ / an error
sum(1.2.3);
sum(1);
/ / right
sum(1.2)
Copy the code
  • Functional expression

In TypeScript type definitions, => is used to represent function definitions, with input types enclosed in parentheses on the left and output types on the right.

In ES6, the => is called the arrow function and is widely used

let mySum = function (x: number, y: number) :number {
    return x + y;
};
// The above code only defines the anonymous function to the right of the equals sign, while the mySum to the left of the equals sign is inferred from the type inference of the assignment operation
// Add the type to mySum manually
let mySum: (x: number, y: number) = > number = function (x: number, y: number) :number {
    return x + y;
};
Copy the code
  • Define the shape of a function with an interface
interface SearchFunc {
    (source: string.subString: string) :boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
    returnsource.search(subString) ! = = -1;
}
Copy the code
  • Optional parameters
function buildName(firstName: string, lastName? :string) {
    if (lastName) {
        return firstName + ' ' + lastName;
    } else {
        returnfirstName; }}let tomcat = buildName('Tom'.'Cat');
let tom = buildName('Tom');
Copy the code

Optional arguments are no longer allowed to be followed by required arguments

function buildName(firstName? :string, lastName: string) {
    if (firstName) {
        return firstName + ' ' + lastName;
    } else {
        returnlastName; }}let tomcat = buildName('Tom'.'Cat');
let tom = buildName(undefined.'Tom');

// index.ts(1,40): error TS1016: A required parameter cannot follow an optional parameter
Copy the code
  • Parameter Default Value

TypeScript recognizes parameters with default values as optional:

function buildName(firstName: string, lastName: string = 'Cat') {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom'.'Cat');
let tom = buildName('Tom');
Copy the code

No longer restricted by “optional parameters must be followed by required parameters” :

function buildName(firstName: string = 'Tom', lastName: string) {
    return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom'.'Cat');
let cat = buildName(undefined.'Cat');
Copy the code
  • The remaining parameters
function push(array: any[], ...items: any[]) {
    items.forEach(function(item) {
        array.push(item);
    });
}
let a = [];
push(a, 1.2.3);
Copy the code
  • overloading

Overloading allows a function to take different numbers or types of arguments and behave differently.

function reverse(x: number) :number;
function reverse(x: string) :string;
function reverse(x: number | string) :number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split(' ').reverse().join(' '));
    } else if (typeof x === 'string') {
        return x.split(' ').reverse().join(' '); }}Copy the code

Type Assertion

Type assertions can be used to manually specify the type of a value

Grammar:

Value AS type or < type > value. The former is recommended

Type assertion purpose

  • Assert a union type as one of the types
// The reason is that (animal as Fish).swim() this code hides the possibility that animal is Cat
// The TypeScript compiler trusts our assertion so there is no compilation error when calling Swim ()
interface Cat {
    name: string;
    run(): void;
}
interface Fish {
    name: string;
    swim(): void;
}
function swim(animal: Cat | Fish) {
    (animal as Fish).swim();
}
const tom: Cat = {
    name: 'Tom'.run() { console.log('run')}}; swim(tom);// Uncaught TypeError: animal.swim is not a function`
Copy the code
  • Assert a parent class as a more concrete subclass

Interface types can only be determined using type assertions to determine if code is AirError class type instanceof

class ApiError extends Error {
    code: number = 0;
}
class HttpError extends Error {
    statusCode: number = 200;
}
function isApiError(error: Error) {
    if (typeof (error as ApiError).code === 'number') {
    // The following is a more appropriate method
    // it is more appropriate to use instanceof, since ApiError is a JavaScript class and instanceof can be used to determine if an error is an instanceof it
    // if (error instanceof ApiError) {
        return true;
    }
    return false;
}
Copy the code
  • Assert any type as any

It most likely hides a true type error, so don’t use as any unless you’re absolutely sure

Syntax: (window as any).foo = 1;

  • Assert any as a concrete type

It is best to assert the return value of a call to an exact type to facilitate subsequent operations (increased maintainability)

function getCacheData(key: string) :any {
    return (window as any).cache[key];
}
interface Cat {
    name: string;
    run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
Copy the code

Restrictions on type assertions

  • A union type can be asserted as one of these types
  • A parent class can be asserted as a subclass
  • Any type can be asserted to be any
  • Any can be asserted to any type
  • In order for A to be asserted as B, either A is compatible with B or B is compatible with A

When Animal is compatible with Cat, they can make type assertions to each other

interface Animal {
    name: string;
}
interface Cat {
    name: string;
    run(): void;
}
function testAnimal(animal: Animal) {
    return (animal as Cat);
}
function testCat(cat: Cat) {
    return (cat as Animal);
}
Copy the code

Double assertion

Never use double assertion unless you absolutely have to

Type assertion vs type conversion

A type assertion is not a conversion and does not really affect the type of a variable

To cast, you need to call the cast method directly

function toBoolean(something: any) :boolean {
    return Boolean(something);
}
toBoolean(1);
// Return true
Copy the code

Type assertion vs type declaration

interface Animal {
    name: string;
}
interface Cat {
    name: string;
    run(): void;
}
const animal: Animal = {
    name: 'tom'
};
// Since Animal is compatible with Cat, we can assign the Animal assertion to Cat to Tom
let tom = animal as Cat;
// let tom: Cat = animal; An error
// Animal can be considered as a parent of Cat. Of course, you cannot assign an instance of a parent to a variable of type subclass.
Copy the code

Animal Cat = Cat animal Cat = Cat animal Animal is assigned to Tom only if Cat is compatible with Animal

In summary, type declarations are more stringent and elegant than type assertions

Type assertion vs generics

Before the change

function getCacheData(key: string) :any {
    return (window as any).cache[key];
}
interface Cat {
    name: string;
    run(): void;
}
const tom = getCacheData('tom') as Cat;
tom.run();
Copy the code

The modified

function getCacheData<T> (key: string) :T {
    return (window as any).cache[key];
}
interface Cat {
    name: string;
    run(): void;
}
const tom = getCacheData<Cat>('tom');
tom.run();
Copy the code

By adding a generic < T > to the getCacheData function, we can more formally implement the constraints on the getCacheData return value. This also removes any from the code, which is the best solution.

Declaration file

When using a third-party library, we need to reference its declaration file to get the corresponding code completion, interface prompts and other functions

New syntactic index

`declare var`Declare global variables`declare function`Declare global methods`declare class`Declaring a global class`declare enum`Declare a global enumeration type`declare namespace`Declare a global object with child attributes`interface`And type declare the global type`export`Export variables`export namespace`Export objects (with child attributes)`export default`On the ES6, it is exported by default`export =`Commonjs export module`export as namespace`The UMD library declares global variables`declare global`Extend global variable`declare module`Extension module`/// <reference />`Three slash instructionCopy the code

What is a declaration statement

If we want to use the third-party library jQuery, a common way to do this is to import jQuery in HTML with a script tag, and then use the global variable $or jQuery.

// declare var does not really define a variable, just the type of the global variable jQuery
// Will only be used for compile-time checks and will be removed from the compilation result
declare var jQuery: (selector: string) = > any;
jQuery('#foo');
Copy the code

JQuery (‘#foo’);

What is a declaration file

Usually we put declarations in a separate file (jquery.d.ts), which is the declaration file

// src/jQuery.d.ts
declare var jQuery: (selector: string) = > any;

// src/index.ts
jQuery('#foo');
Copy the code

Declaration files must have a.d.ts suffix

In general, TS parses all *. Ts files in a project, including those ending in.d.ts. So when we put jquery.d.ts in the project, all the other *.ts files will get the jQuery type definition

The/path/to/project ├ ─ ─ the SRC | ├ ─ ─ but ts | └ ─ ─ jQuery. Which s └ ─ ─ tsconfig. JsonCopy the code

If you still can’t parse, check the files, include, and exclude configurations in tsconfig.json to make sure they include jquery.d. ts files

Third Party Declaration Document

It is recommended that you use @Types to manage third-party library declaration files uniformly.

The use of @types is simple, simply install the corresponding declaration module with NPM, e.g

npm install @types/jquery --save-dev

Written declaration document

The global variable

When using declarations of global variables, no configuration is required if installed as NPM install@types/XXX –save-dev. If you want to store the declaration file directly in the current project, it is recommended that (jquery.d.ts) and other source code be placed in the SRC directory (or the corresponding source directory).

Global variable declaration files have the following syntax:

`declare var`Declare global variables`declare function`Declare global methods`declare class`Declaring a global class`declare enum`Declare a global enumeration type`declare namespace`Declare a global object with child attributes` interface and type `Declaring global typesCopy the code
declare var

In general, global variables are non-modifiers, so in most cases you should use const instead of var or let.

// src/jQuery.d.ts

declare const jQuery: (selector: string) = > any;

// jQuery('#foo');
// Use the jQuery type defined by Declare const to prohibit modifying this global variable
// ERROR: Cannot assign to 'jQuery' because it is a constant or a read-only property.

jQuery = function(selector) {
    return document.querySelector(selector);
};

// Note: only types can be defined in declarations. Do not define specific implementations in declarations
// declare const jQuery = function(selector) {
// return document.querySelector(selector);
// };
// ERROR: An implementation cannot be declared in ambient contexts.
Copy the code
declare function

The type used to define a global function. JQuery is a function, so you can use function as well

Example:

// src/jQuery.d.ts
declare function jQuery(selector: string) :any;

// src/index.ts
jQuery('#foo');
Copy the code

Function overloading is also supported in declarations of function types

// src/jQuery.d.ts
declare function jQuery(selector: string) :any;
declare function jQuery(domReadyCallback: () => any) :any;

// src/index.ts
jQuery('#foo');
jQuery(function() {
    alert('Dom Ready! ');
});
Copy the code
declare class

When a global variable is a class, we use declare Class to define its type

Example:

// src/Animal.d.ts
declare class Animal {
    name: string;
    constructor(name: string);
    sayHi(): string;
}

// src/index.ts
let cat = new Animal('Tom');
Copy the code

Declare Class statements can also only be used to define types (as can DECLARE var), not concrete implementations, such as defining concrete implementations of the sayHi method

Examples of errors:

// src/Animal.d.ts
declare class Animal {
    name: string;
    constructor(name: string);
    sayHi() {
        return `My name is The ${this.name}`;
    };
    // ERROR: An implementation cannot be declared in ambient contexts.
}
Copy the code
declare enum

Enumeration types defined using declare enum also called Ambient Enums

// src/Directions.d.ts
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}

// src/index.ts
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right]
Copy the code
declare namespace

Namespace is a key word created by TS in the early days to solve the modularity problem

JQuery is a global variable. It is an object that provides a jquery. ajax method to call

Inside the Declare Namespace, we declare functions directly using function Ajax instead of using Declare Function Ajax. Similarly, we can use const, class, enum, etc

// src/jQuery.d.ts
declare namespace jQuery {
    function ajax(url: string, settings? :any) :void;
    const version: number;
    class Event {
        blur(eventType: EventType): void
    }
    enum EventType {
        CustomClick
    }
}

// src/index.ts
jQuery.ajax('/api/get_something');
console.log(jQuery.version);
const e = new jQuery.Event();
e.blur(jQuery.EventType.CustomClick);
Copy the code
Interface and type

In a type declaration file, we can declare a global interface or type directly using interface or type

// src/jQuery.d.ts
interfaceAjaxSettings { method? :'GET' | 'POST'data? :any;
}
declare namespace jQuery {
    function ajax(url: string, settings? : AjaxSettings) :void;
}

// src/index.ts
let settings: AjaxSettings = {
    method: 'POST'.data: {
        name: 'foo'}}; jQuery.ajax('/api/post_something', settings);
Copy the code
Preventing naming conflicts

Interfaces or types exposed in the outermost layer are used as global types throughout the project, and we should minimize the number of global variables or global types. Therefore, it is best to put them under namespace

A statement to merge
// src/jQuery.d.ts
declare function jQuery(selector: string) :any;
declare namespace jQuery {
    function ajax(url: string, settings? :any) :void;
}

// src/index.ts
jQuery('#foo');
jQuery.ajax('/api/get_something');
Copy the code
NPM package

NPM package declaration file has the following syntax:

exportExport variablesexportNamespace Exports objects with child attributesexportDefault ES6 Exports data by defaultexport= CommonJS Export moduleCopy the code
export

The NPM package declaration file is quite different from the global variable declaration file. In the declaration file of the NPM package, using DECLARE no longer declares a global variable, but only a local variable in the current file. These type declarations are applied only after export is exported in the declaration file and then imported by the user import

// types/foo/index.d.ts
export const name: string;
export function getName() :string;
export class Animal {
    constructor(name: string);
    sayHi(): string;
}
export enum Directions {
    Up,
    Down,
    Left,
    Right
}
export interface Options {
    data: any;
}
Copy the code

The corresponding import and use modules should look like this:

import { name, getName, Animal, Directions, Options } from 'foo';

console.log(name);
let myName = getName();
let cat = new Animal('Tom');
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
let options: Options = {
    data: {
        name: 'foo'}};Copy the code
Mix DECLARE and export

Declare multiple variables with DECLARE and export at the end

// types/foo/index.d.ts

declare const name: string;
declare function getName() :string;
declare class Animal {
    constructor(name: string);
    sayHi(): string;
}
declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
interface Options {
    data: any;
}

export { name, getName, Animal, Directions, Options };
Copy the code

Note: Interface does not require declare

export namespace

The export namespace command is used to export an object with child attributes

// types/foo/index.d.ts

export namespace foo {
    const name: string;
    namespace bar {
        function baz() :string; }}Copy the code
// src/index.ts

import { foo } from 'foo';

console.log(foo.name);
foo.bar.baz();
Copy the code
export default

In the ES6 module system, export default can be used to export a default value, which can be imported by using import foo from ‘foo’ instead of import {foo} from ‘foo’

Only function, class, and interface can be exported by default. Other variables need to be defined before being exported by default

// types/foo/index.d.ts

export default function foo() :string;
Copy the code
// src/index.ts

import foo from 'foo';

foo();
Copy the code

For other default exports, the export statement is typically placed at the top of the declaration file

export default Directions;

declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
Copy the code
export =

In the CommonJS specification, we export a module in the following way

// Export as a whole
module.exports = foo;
// Single export
exports.bar = bar;
Copy the code

In TS, there are several ways to import such module exports. The first is const… = require

// Whole import
const foo = require('foo');
// Single import
const bar = require('foo').bar;
Copy the code

The second way is to import… From, note that for the overall export, import * as is required

// Whole import
import * as foo from 'foo';
// Single import
import { bar } from 'foo';
Copy the code

The third way is to import… Require, which is also the official ts recommendation

// Whole import
import foo = require('foo');
// Single import
import bar = foo.bar;
Copy the code

For libraries that use the CommonJS specification, if you want to write a type declaration file for it, you need to use the syntax export =

// types/foo/index.d.ts

export = foo;

declare function foo() :string;
declare namespace foo {
    const bar: number;
}
Copy the code

UMD library

export as namespace

Generally, when you use export as namespace (for declaring additional global variables), you can first have the declaration file of NPM package and then add an export as namespace statement based on it to declare a declared variable as a global variable, as shown in the following example

// types/foo/index.d.ts

export as namespace foo;
// export default foo;
export = foo;

declare function foo(): string;
declare namespace foo {
    const bar: number;
}
Copy the code

Expand global variables directly

Some third-party libraries extend a global variable, but the type of the global variable is not updated, resulting in TS compilation errors. In this case, the type of the global variable needs to be extended. For example, extend String

interface String {
    prependHello(): string;
}

'foo'.prependHello();
Copy the code

You can also use declare Namespace to add a type declaration to an existing namespace

// types/jquery-plugin/index.d.ts

declare namespace JQuery {
    interface CustomOptions {
        bar: string; }}interface JQueryStatic {
    foo(options: JQuery.CustomOptions): string;
}
Copy the code
// src/index.ts

jQuery.foo({
    bar: ' '
});
Copy the code

Extend global variables in NPM packages or UMD libraries

declare global

You can extend the types of global variables in the NPM package or in the UMD library declaration file

// types/foo/index.d.ts

declare global {
    interface String {
        prependHello(): string; }}// Note that even though this declaration file does not need to export anything, it still needs to export an empty object to tell the compiler that this is a module declaration file
// Instead of a global variable declaration file
export {};
Copy the code
// src/index.ts

'bar'.prependHello();
Copy the code

Module plug-ins

Ts provides a syntax Declare Module that can be used to extend the type of an existing module

// types/moment-plugin/index.d.ts

import * as moment from 'moment';

declare module 'moment' {
    export function foo() :moment.CalendarKey;
}
Copy the code
// src/index.ts

import * as moment from 'moment';
import 'moment-plugin';

moment.foo();
Copy the code

The Declare Module can also be used to declare multiple module types at once in a file

// types/foo-bar.d.ts

declare module 'foo' {
    export interface Foo {
        foo: string; }}declare module 'bar' {
    export function bar() :string;
}
Copy the code
// src/index.ts

import { Foo } from 'foo';
import * as bar from 'bar';

let f: Foo;
bar.bar();
Copy the code

Declare dependencies in files

One declaration file sometimes relies on the types in another declaration file. For example, in the previous declare Module example, we imported moment into the declaration file and used moment.CalendarKey

// types/moment-plugin/index.d.ts

import * as moment from 'moment';

declare module 'moment' {
    export function foo() :moment.CalendarKey;
}
Copy the code
Three slash instruction

Like an import in a declaration file, it can be used to import another declaration file. The difference with import is that we need to replace import with a triple slash directive if and only if:

  • When we write a declaration file for a global variable
  • When we need to rely on a global variable declaration file
Write a global variable declaration file

These scenarios sound like a mouthful, but they actually make sense — the import and export keywords are not allowed in declarations of global variables. Once present, it is treated as an NPM package or UMD library and is no longer a declaration file for global variables. Therefore, when writing a global variable declaration file, we must use the triple slash directive if we need to reference another library type

// types/jquery-plugin/index.d.ts

/// <reference types="jquery" />

declare function foo(options: JQuery.AjaxSettings) :string;
Copy the code
// src/index.ts

foo({});
Copy the code

The jquery. AjaxSettings type is used in the declaration file. The jquery. AjaxSettings type is used in the declaration file

Note that the triple slash directive must be placed at the top of the file, and only one or more lines of comments are allowed before the triple slash directive

A declaration file that depends on a global variable

In another scenario, when we need to rely on the declaration file of a global variable, we must use the triple slash directive because global variables cannot be imported through import

// types/node-plugin/index.d.ts

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

export function foo(p: NodeJS.Process) :string;
Copy the code
// types/node-plugin/index.d.ts

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

export function foo(p: NodeJS.Process) :string;
Copy the code
Split declaration file

When our global variable declaration file is too large, we can improve the maintainability of our code by splitting it into multiple files and then importing them one by one in an entry file. JQuery’s declaration file, for example, does this

// node_modules/@types/jquery/index.d.ts

/// <reference types="sizzle" />
/// <reference path="JQueryStatic.d.ts" />
/// <reference path="JQuery.d.ts" />
/// <reference path="misc.d.ts" />
/// <reference path="legacy.d.ts" />

export = jQuery;
Copy the code

Automatically generate declaration files

TSC xx. Ts -d or TSC xx. Ts –declaration TSC xx

The difference is that types is used to declare a dependency on another library, while PATH is used to declare a dependency on another file

Method 2: Add the declaration option to tsconfig.json. Tsconfig. json is used as an example

{
    "compilerOptions": {
        "module": "commonjs"."outDir": "lib"."declaration": true}}Copy the code

Release statement file

Once we have written the declaration file for a library, the next step is to publish it.

There are two options:

  • Put the declaration file with the source code
  • Publish the declaration file to@types

Built-in objects

There are many built-in objects in JavaScript that can be treated directly as defined types in TypeScript.

Built-in objects are objects that exist in the Global scope according to the standard. Standards here refer to ECMAScript and standards in other environments such as DOM

ECMAScript built-in object

The ECMAScript standard provides built-in objects such as Boolean, Error, Date, RegExp, etc.

let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date(a);let r: RegExp = /[a-z]/;
Copy the code

More built-in object core library definitions

DOM and BOM built-in objects

DOM and BOM provide built-in objects such as Document, HTMLElement, Event, and NodeList.

/ / the commonly used
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click'.function(e: MouseEvent) {
  // Do something
});
Copy the code

Core Library Definition

The TypeScript core library definition file

The TypeScript core library definition files define the types needed by all browser environments and are preconfigured in TypeScript.

Written in TypeScript Node. Js

Node.js is not part of the built-in object. If you want to write Node.js in TypeScript, you need to import a third-party declaration file: NPM install @types/ Node –save-dev

The advanced

Type the alias

Type aliases are often used to combine types

type Name = string;
type NameResolver = () = > string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver) :Name {
    if (typeof n === 'string') {
        return n;
    } else {
        returnn(); }}Copy the code

String literal type

The string literal type is used to constrain the value to one of several strings.

type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
    // do something
}
handleEvent(document.getElementById('hello'), 'scroll');  / / no problem
handleEvent(document.getElementById('world'), 'dblclick'); // Error: event cannot be 'dblclick'
// index.ts(7,47): error TS2345: Argument of type '"dblclick"' is not assignable to parameter of type 'EventNames'.
Copy the code

Note that both type aliases and string literal types are defined using type.

tuples

Arrays merge objects of the same type, while tuples merge objects of different types.

Tuples originated in functional programming languages such as F#, where tuples are frequently used

let tom: [string.number];
tom[0] = 'Tom';
tom[1] = 25;
tom[0].slice(1);
tom[1].toFixed(2);
Copy the code

Out-of-bounds arrays: When an out-of-bounds element is added, its type is limited to the union type of each type in the tuple

let tom: [string.number];
tom = ['Tom'.25];
tom.push('male');
tom.push(true);
// Argument of type 'true' is not assignable to parameter of type 'string | number'.
Copy the code

The enumeration

The enumeration (Enum) type applies to scenarios where the value is limited within a certain range, for example, there are only seven days in a week, and the color is limited to red, green and blue

Enumerators are assigned an increasing number starting from 0, and the enumeration value is also mapped backwards to the enumeration name

enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

console.log(Days["Sun"= = =0); // true
console.log(Days["Mon"= = =1); // true
console.log(Days["Tue"= = =2); // true
console.log(Days["Sat"= = =6); // true

console.log(Days[0= = ="Sun"); // true
console.log(Days[1= = ="Mon"); // true
console.log(Days[2= = ="Tue"); // true
console.log(Days[6= = ="Sat"); // true
Copy the code

Manual assignment

TypeScript doesn’t notice if an unmanually assigned enumeration item duplicates the manually assigned one

enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"= = =3); // true
console.log(Days["Wed"= = =3); // true
console.log(Days[3= = ="Sun"); // false
console.log(Days[3= = ="Wed"); // true
Copy the code

Manually assigned enumerations may not be numbers, in which case type assertions are needed to make TSC ignore type checking (the compiled JS is still available) :

enum Days {Sun = 7, Mon, Tue, Wed, Thu, Fri, Sat = <any>"S"};
Copy the code

Manually assigned enumerations can also be decimal or negative, with subsequent unmanually assigned enumerations still increasing by 1

enum Days {Sun = 7, Mon = 1.5, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"= = =7); // true
console.log(Days["Mon"= = =1.5); // true
console.log(Days["Tue"= = =2.5); // true
console.log(Days["Sat"= = =6.5); // true
Copy the code

Constant term and computed term

There are two types of enumeration items: constant (constant member) and computed (computed member)

// Correct example
enum Color {Red, Green, Blue = "blue".length};
// Error example
// If the computed item is immediately followed by an item that is not manually assigned, it will report an error because it cannot obtain the initial value
enum Color {Red = "red".length, Green, Blue};
// index.ts(1,33): error TS1061: Enum member must have initializer.
// index.ts(1,40): error TS1061: Enum member must have initializer.
Copy the code

Enumerators are treated as constants if:

There is no initialization function and the previous enumerator is a constant. In this case, the current enumerator’s value is the value of the previous enumerator plus one. The first enumeration element is an exception. If it has no initialization method, its initial value is 0.

Enumerator members are initialized with constant enumeration expressions. Constant enumerated expressions are a subset of TypeScript expressions that can be evaluated at compile time. An expression is a constant enumerated expression when it satisfies one of the following conditions:

Numeric literal

Reference to a previously defined constant enumerator (which can be defined in a different enumeration type). If the member is defined in the same enumeration type, it can be referenced by an unqualified name

A bracketed constant enumeration expression

The +, -, ~ unary operators are applied to constant enumeration expressions

The +, -, *, /, %, < <, > >, > > >, &, |, ^ binary operators, constant enumeration expression as its an operation object. If the constant enumeration expression evaluates to NaN or Infinity, an error is reported at compile time

Enumerators in all other cases are treated as values that need to be computed.

Constant enumeration

const enum Directions {
    Up,
    Down,
    Left,
    Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
Copy the code

Constant enumerations differ from regular enumerations in that they are removed at compile time and cannot contain computed members.

If a calculated member is included, an error is reported at compile time:

const enum Color {Red, Green, Blue = "blue".length};
// index.ts(1,38): error TS2474: In 'const' enum declarations member initializer must be constant expression.
Copy the code

External enumeration (Ambient Enums)

declare enum Directions {
    Up,
    Down,
    Left,
    Right
}
let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
Copy the code

It is also possible to use both declare and const:

declare const enum Directions {
    Up,
    Down,
    Left,
    Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
Copy the code

class

The concept of class

- Class: defines the abstract characteristics of a thing, including its properties and methods. - Object: an instance of a Class, generated through new. - Three main features of OOP: Encapsulation, inheritance, and Polymorphism - Encapsulation: Hide the details of manipulation of data, exposing only the external interface. External callers don't need (and can't) know the details, so they can access the object through the interface provided externally, and at the same time, it also ensures that the external can't arbitrarily change the data inside the object. Subclasses inherit from their superclass. As well as having all the characteristics of their superclass, subclasses are also more specific: Polymorphism: the result of inheritance is that related different classes can respond differently to the same method. For example, Cat and Dog both inherit from Animal, but each implements its own eat method. For a particular instance, we can call the eat method without knowing whether it's Cat or Dog, and the program will automatically figure out how to perform the eat-accessors: Modifiers are keywords that qualify the nature of a member or type. For example, public stands for a public property or method-abstract Class: an Abstract Class is a base Class that other classes inherit, and an Abstract Class is not allowed to be instantiated. Abstract methods in an abstract class must be implemented in subclasses - Interfaces: properties or methods that are common between classes and can be abstracted into an interface. An interface can be implemented by a class. A class can only inherit from another class, but can implement multiple interfacesCopy the code

ES6 class usage

Properties and methods

Use class to define classes and constructor to define constructors.

The constructor is automatically called when a new instance is generated from new

class Animal {
    public name;
    constructor(name) {
        this.name = name;
    }
    sayHi() {
        return `My name is The ${this.name}`; }}let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
Copy the code
Class inheritance

The extends keyword is used for inheritance, and the super keyword is used in subclasses to call the constructors and methods of the parent class

class Cat extends Animal {
  constructor(name) {
    super(name); // Call parent class constructor(name)
    console.log(this.name);
  }
  sayHi() {
    return 'Meow, ' + super.sayHi(); SayHi ()}}let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom
Copy the code
accessor

Use getters and setters to change the assignment and read behavior of properties

class Animal {
  constructor(name) {
    this.name = name;
  }
  get name() {
    return 'Jack';
  }
  set name(value) {
    console.log('setter: '+ value); }}let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack
Copy the code
A static method

Methods decorated with static modifiers are called static methods; they do not need to be instantiated, but are called directly from the class

class Animal {
  static isAnimal(a) {
    return a instanceofAnimal; }}let a = new Animal('Jack');
Animal.isAnimal(a); // true
a.isAnimal(a); // TypeError: a.isAnimal is not a function
Copy the code

ES7 class usage

Instance attributes

In ES6, instance attributes can only be defined in the constructor this. XXX; in THE ES7 proposal, they can be defined directly in the class

class Animal {
    name = 'Jack';
    constructor() {
        / /...}}let a = new Animal();
console.log(a.name); //Jack
Copy the code
Static attributes

In the ES7 proposal, you can define a static property using static

class Animal {
    static num = 42;
    constructor() {
        // ...}}console.log(Animal.num); / / 42
Copy the code

TypeScript class usage

Public, private and protected
  • Properties or methods decorated by public are public and can be accessed anywhere. By default, all properties and methods are public
  • A property or method modified by private is private and cannot be accessed outside the class in which it is declared
  • Protected attributes or methods are protected, just like private, except that they are accessible in subclasses
Parameter properties

Modifiers and readonly can also be used in constructor arguments to assign a value to the property as defined in the class, making code more concise

class Animal {
  // public name: string;
  public constructor(public name) {
    // this.name = name;}}Copy the code
readonly

A read-only property keyword that is allowed only in a property declaration or index signature or constructor

class Animal {
  readonly name;
  public constructor(name) {
    this.name = name; }}let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
// index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property.
Copy the code

It would be more elegant

class Animal {
  // public readonly name;
  public constructor(public readonly name) {
    // this.name = name;}}Copy the code
An abstract class

Abstract is used to define abstract classes and their abstract methods

Abstract classes are not allowed to be instantiated.

abstract class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
  public abstract sayHi();
}
let a = new Animal('Jack');
Ts (9,11): error TS2511: Cannot create an instance of the abstract class 'Animal'.
Copy the code

Abstract methods in abstract classes must be implemented by subclasses, ex:

abstract class Animal {
  public name;
  public constructor(name) {
    this.name = name;
  }
  public abstract sayHi();
}
// Subclasses implement abstract classes
class Cat extends Animal {
  public sayHi() {
    console.log(`Meow, My name is The ${this.name}`); }}let cat = new Cat('Tom');
Copy the code

The type of the class

Adding TypeScript types to classes is simple, similar to interfaces

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  sayHi(): string {
    return `My name is The ${this.name}`; }}let a: Animal = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
Copy the code

Classes and interfaces

Class implementation interface

Implements is an important concept in object orientation. Features that are common to different classes can be extracted as interfaces, which are implemented using the implements keyword. This feature greatly improves object-oriented flexibility.

For example, a door is a class, and an anti-theft door is a subclass of a door. If the security door has an alarm function, we can simply add an alarm method to the security door. If you have another class, a car, that also has the function of alarm, you can consider extracting the alarm, as an interface, security door and car to achieve it.

interface Alarm {
    alert(): void;
}
class door {}class SecurityDoor extends door implements Alarm {
    alert() {
        console.log('SecurityDoor alert'); }}class Car implements Alarm {
    alert() {
        console.log('Car alert'); }}Copy the code

A class implements multiple interfaces

interface Alarm {
    alert(): void;
}
interface Light {
    lightOn(): void;
    lightOff(): void;
}
class Car implements Alarm.Light {
    alert() {
        console.log('Car alert');
    }
    lightOn() {
        console.log('Car light on');
    }
    lightOff() {
        console.log('Car light off'); }}Copy the code

Interface Inheritance interface

LightableAlarm inherits Alarm and has two new methods, lightOn and lightOff, in addition to the alert method.

interface Alarm {
    alert(): void;
}
interface LightableAlarm extends Alarm {
    lightOn(): void;
    lightOff(): void;
}
Copy the code

Interface inheritance class

In common object-oriented languages, interfaces cannot inherit from classes, but in TypeScript they can

  • Class can be used as a class (new xxCreate an instance of it.
  • A class can be used as a type: xxIndicates the type of the parameter.
interface PointInstanceType {
    x: number;
    y: number;
}
/ / equivalent to the
class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y; }}Copy the code

When we declare Interface Point3d extends Point, Point3d actually inherits the type of an instance of the class Point.

In other words, one interface, Point3d, is defined to inherit from another interface, PointInstanceType.

So there is no essential difference between “interface inheriting class” and “interface inheriting interface”.

class Point {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y; }}interface PointInstanceType {
    x: number;
    y: number;
}
// Equivalent to Interface Point3d extends PointInstanceType
interface Point3d extends Point {
    z: number;
}
let point3d: Point3d = {x: 1.y: 2.z: 3};
Copy the code

In addition to constructors, static properties or methods are not included. (The type of an instance should certainly not include constructors, static properties, or static methods.)

That is, declare that the class creates a type that contains instance properties and instance methods

class Point {
    /** static attribute, coordinate system origin */
    static origin = new Point(0.0);
    /** Static method, calculate the distance from the origin */
    static distanceToOrigin(p: Point) {
        return Math.sqrt(p.x * p.x + p.y * p.y);
    }
    /** Instance attribute, X-axis value */
    x: number;
    /** Instance attribute, y value */
    y: number;
    /** constructor */
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    /** instance method, print this point */
    printPoint() {
        console.log(this.x, this.y); }}interface PointInstanceType {
    x: number;
    y: number;
    printPoint(): void;
}
let p1: Point;
let p2: PointInstanceType;
Copy the code

Generics

Generics are the feature of defining functions, interfaces, or classes without specifying a specific type in advance.

Simple example

function createArray<T> (length: number, value: T) :Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
// You can specify or not specify, and type inference will be calculated automatically
createArray<string> (3.'x'); // ['x', 'x', 'x']
createArray(3.'x'); // ['x', 'x', 'x']
Copy the code

Multiple type parameters

function swap<T.U> (tuple: [T, U]) :U.T] {
    return [tuple[1], tuple[0]];
}
// A tuple used to exchange input
swap([7.'seven']); // ['seven', 7]
Copy the code

Generic constraint

We use extends to constrain that the generic T must conform to the Lengthwise shape of the interface, that is, it must contain the Length attribute.

If the arG passed in does not contain length when calling loggingIdentity, an error will be reported at compile time:

interface Lengthwise {
    length: number;
}
function loggingIdentity<T extends Lengthwise> (arg: T) :T {
    console.log(arg.length);
    return arg;
}
loggingIdentity(7);
// index.ts(10,17): error TS2345: Argument of type '7' is not assignable to parameter of type 'Lengthwise'.
Copy the code

Multiple type parameters can also constrain each other by requiring T to inherit from U, which ensures that fields on U that do not exist in T will not appear

function copyFields<T extends U.U> (target: T, source: U) :T {
    for (let id in source) {
        target[id] = (<T>source)[id];
    }
    return target;
}
let x = { a: 1.b: 2.c: 3.d: 4 };
copyFields(x, { b: 10.d: 20 });
Copy the code

A generic interface

interface CreateArrayFunc<T> {
    (length: number.value: T): Array<T>;
}
let createArray: CreateArrayFunc<any>;
createArray = function<T> (length: number, value: T) :Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
createArray(3.'x'); // ['x', 'x', 'x']
Copy the code

A generic class

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) = > T;
}
let myGenericNumber = new GenericNumber<number> (); myGenericNumber.zeroValue =0;
myGenericNumber.add = function(x, y) { return x + y; };
Copy the code

The default type of a generic parameter

We can specify default types for type parameters in generics. This default type comes into play when type parameters are not specified directly in the code when using generics and cannot be inferred from the actual value parameters

function createArray<T = string> (length: number, value: T) :Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}
Copy the code

A statement to merge

If you define two functions, interfaces, or classes with the same name, they are merged into one type

Combination of functions

// Overloading defines multiple function types
function reverse(x: number) :number;
function reverse(x: string) :string;
function reverse(x: number | string) :number | string {
    if (typeof x === 'number') {
        return Number(x.toString().split(' ').reverse().join(' '));
    } else if (typeof x === 'string') {
        return x.split(' ').reverse().join(' '); }}Copy the code

Merging interfaces

Attributes in interfaces are simply merged into one interface when merged

interface Alarm {
    price: number;
    weight: number;
}
Copy the code

The type of the merged attribute must be unique

interface Alarm {
    price: number;
}
interface Alarm {
    price: string;  // If the type is inconsistent, an error will be reported
    weight: number;
}
/ / index. Ts (5, 3) : error TS2403: Subsequent variable declarations must have the same type. Variable 'price' must be of type 'number', but here has type 'string'.
Copy the code

Merging methods in interfaces is the same as merging functions:

interface Alarm {
    price: number;
    alert(s: string) :string;
}
interface Alarm {
    weight: number;
    alert(s: string.n: number) :string;
}
Copy the code

The equivalent of

interface Alarm {
    price: number;
    weight: number;
    alert(s: string) :string;
    alert(s: string.n: number) :string;
}
Copy the code

Such merger

interface Alarm {
    price: number;
    weight: number;
    alert(s: string) :string;
    alert(s: string.n: number) :string;
}
Copy the code

engineering

Code review

In January 2019, TypeScirpt officially decided to fully adopt ESLint as a code checking tool and created a new project, typescript-ESLint, TypeScript file parser @typescript-eslint/parser and related configuration options @typescript-eslint/eslint-plugin are provided.

What is code review

Code inspection is mainly used to find code errors and unify the code style.

In JavaScript projects, we generally use ESLint for code checking, which greatly enriches the scope with its plug-in features and can even be used to check typescript code when paired with typescript-ESLint

Why code review

After compiling with TSC and checking with ESLint, the following error message is reported

var myName = 'Tom';
// esLint reports an error message:
// Unexpected var, use let or const instead.eslint(no-var)
console.log(`My name is ${myNane}`);
// TSC error message:
// Cannot find name 'myNane'. Did you mean 'myName'?
// esLint reports an error message:
// 'myNane' is not defined.eslint(no-undef)
console.log(`My name is ${myName.toStrng()}`);
// TSC error message:
// Property 'toStrng' does not exist on type 'string'. Did you mean 'toString'?
Copy the code
  • ES6Has more advanced syntaxletconstAt this point, you can passeslintCheck it out and prompt us to use itletconstRather thanvar
  • eslintUnable to identifymyNameWhat methods exist
  • eslintYou can find sometscDon’t care about errors, check out some potential problems, so code review is very important.

Use ESLint in TypeScript

Install ESLint in the project

npm install --save-dev eslint

Since ESLint uses Espree for syntax parsing by default, some TypeScript syntax is not recognized, so we need to install @typescript-esLint /parser instead of the default parser. Don’t forget to install TypeScript as well

npm install --save-dev typescript @typescript-eslint/parser

@typescript-eslint/eslint-plugin It supplements the esLint default rules by providing some additional rules applicable to ts syntax

npm install --save-dev @typescript-eslint/eslint-plugin

Creating a Configuration File

ESLint needs a configuration file to determine which rules to check, typically named.eslintrc.js or.eslintrc.json

When running ESLint to check a file, it will first try to read the configuration file in the directory in which the file is stored, then go up one level and combine the found configuration as the configuration of the file being checked

Create.eslintrc.js in the root directory of your project with the following contents:

module.exports = {
    parser: '@typescript-eslint/parser'.plugins: ['@typescript-eslint'].rules: {
        // Do not use var
        'no-var': "error".// Use interface instead of type
        '@typescript-eslint/consistent-type-definitions': [
            "error"."interface"]}}Copy the code

In the configuration above, we specified two rules, where no-var is a rule native to ESLint, @typescript-eslint/consistent-type-definitions is a new rule added to @typescript-eslint/eslint-plugin.

Either OFF, WARN, or ERROR, which indicates closure, warning, or error

The meanings of disable, warning, and error are as follows: Disable: This rule is disabled. Warning: An error message is displayed during code check, but it does not affect the exit code error message: When an error is found, not only will an error message be printed, but the exit code will be set to 1.Copy the code
Check a TS file

Create a new file index.ts

var myName = 'Tom';
type Foo = {};
Copy the code

Perform. / node_modules /. Bin/eslint index. The ts

Error received:

/path/to/index.ts
  1:1  error  Unexpected var, use let or const instead  no-var
  2:6  error  Use an `interface` instead of a `type'@typescript-eslint/consistent-type-definitions * 2 problems (2 errors, 0 warnings) 1 error and 0 warnings potentially fixable with the `--fix` option.Copy the code

It is inconvenient to execute such a long script at a time, but we can simplify this step by creating an NPM script by adding a script to package.json

{
    "scripts": {
        "eslint": "eslint index.ts"}}Copy the code

Simply execute NPM run ESLint

Review the TS files for the entire project

Project source files are typically stored in the SRC directory, so change the ESLint script in package.json to check a directory instead. Since esLint does not check files with the.ts suffix by default, the argument — ext.ts is required

{
    "scripts": {
        "eslint": "eslint src --ext .ts"}}Copy the code

NPM run esLint checks all files with the.ts suffix in the SRC directory

Integrate ESLint checking with VSCode

Plugin name: ESLint

The ESLint plugin in VSCode does not check the.ts suffix by default, you need to add the following configuration in “File => Preferences => Settings => Workspace” (you can also create a configuration file in the project root directory.vscode/settings.json) :

{
    "eslint.validate": [
        "javascript"."javascriptreact"."typescript"]."typescript.tsdk": "node_modules/typescript/lib"
}
Copy the code

Ts file, move the mouse pointer to the red prompt, you can see the error message

Plus automatic repair when saving, modify the configuration file

{
    "editor.codeActionsOnSave": {
        "source.fixAll": true."source.fixAll.eslint": true
    },
    "eslint.validate": [
        "javascript"."javascriptreact"."typescript"]."typescript.tsdk": "node_modules/typescript/lib"
}
Copy the code
Use Prettier to fix formatting errors

ESLint includes checks for code formats such as Spaces, semicolons, and so on. But a more advanced tool for formatting code in the front-end community is Prettier

Prettier focuses on formatting code, parsing it so that everyone writes the same way

Install the Prettier

npm install --save-dev prettier

Create a file prettier. Config. js that contains the configuration items for Prettier. There are few configuration items for Prettier, so I recommend one

// prettier.config.js or .prettierrc.js
module.exports = {
    // A line of up to 100 characters
    printWidth: 100.// Use 4 Spaces for indentation
    tabWidth: 4.// Instead of indentation, use Spaces
    useTabs: false.// A semicolon is required at the end of the line
    semi: true.// Use single quotes
    singleQuote: true.// The key of the object is quoted only when necessary
    quoteProps: 'as-needed'.JSX uses double quotes instead of single quotes
    jsxSingleQuote: false.// Do not need a comma at the end
    trailingComma: 'none'.// Spaces are required at the beginning and end of braces
    bracketSpacing: true.// JSX tag Angle brackets require line breaks
    jsxBracketSameLine: false.// Arrow functions with only one argument also need parentheses
    arrowParens: 'always'.// The range in which each file is formatted is the entire content of the file
    rangeStart: 0.rangeEnd: Infinity.// There is no need to write @prettier at the beginning of the file
    requirePragma: false.// There is no need to automatically insert @prettier at the beginning of a file
    insertPragma: false.// Use the default line folding standard
    proseWrap: 'preserve'.// Depending on the display style, HTML should be folded or not
    htmlWhitespaceSensitivity: 'css'.// Use lf for line breaks
    endOfLine: 'lf'
};
Copy the code
ESLint configuration using AlloyTeam

There are too many ESLint native rules and @typescript-eslint/eslint-plugin rules, and some of the native rules don’t support typescript well and need to be disabled

The installation

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

Add it under.eslintrc.js

module.exports = {
    extends: [
        'alloy'.'alloy/typescript',].env: {
        // Your environment variables (containing multiple predefined global variables)
        // Your environments (which contains several predefined global variables)
        //
        // browser: true,
        // node: true,
        // mocha: true,
        // jest: true,
        // jquery: true
    },
    globals: {
        // Your global variable (set to false means it is not allowed to be reassigned)
        // Your global variables (setting to false means it's not allowed to be reassigned)
        //
        // myGlobal: false
    },
    rules: {
        // Customize your rules
        // Customize your rules}};Copy the code
Use ESLint to check TSX files

Install eslint plugin – react:

npm install --save-dev eslint-plugin-react

Eslint adds the.tsx suffix to package.json

{
    "scripts": {
        "eslint": "eslint src --ext .ts,.tsx"}}Copy the code

New typescriptreact check in VSCode configuration

{
    "eslint.validate": [
        "typescriptreact"],}Copy the code
Conclusion: if encounter what question or suggestion, can leave a message comment directly! The author will reply immediately

If you feel small white this article is good or helpful to you, look forward to your one key three even 💫! ❤ ️ ni ~