Introduction (I don’t know what it’s called)

Hey, hey, hey, hey, hey, hey, hey, hey, hey. I’m back.

Last week, we talked about unifying the stack. The first thing we want to do is unify TypeScript, so we have this series of articles. Today, we have finished the advanced part, and we are going to post it to help those who can’t.

Statement (or this statement) : ✨ this series of articles as the basis of the tutorial, does not involve deep things, more suitable for beginners to see, if the students have already met, read this paragraph of text can run away 😋.

Use of built-in objects

There are many built-in objects in JavaScript that can be used directly in JavaScript programs, and TypeScript extends those built-in objects as well

Global objects, or standard built-in objects, are not to be confused with “global objects”. When I say global objects, I mean objects in the global scope.

ECMAScript built-in objects

The ECMAScript standard provides built-in objects such as: Object, Error, Number, Date, Boolean, Array, etc.

const boolean: Boolean = new Boolean(true)
const date: Date = new Date()
const n: Number = new Number(20)Copy the code

For more built-in objects, see JavaScript standard built-in objects

DOM and BOM built-in objects

DOM and BOM are commonly used:

Document, HTMLElement, Event, NodeList, Node, etc., are useful in DOM operations

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

See document Object Model (DOM) for more


function

This in the function

In JavaScript, the use of this may sometimes not be what you think it is

But the good news is that TypeScript will tell you if you’re using this correctly.

In the following example, SVGElement is a window method, so the editor will report an error:

document.querySelector('body').addEventListener('click'.function (event:MouseEvent) {
  this.nodeName
  this.SVGElement//Property 'SVGElement' does not exist on type 'HTMLBodyElement'.})Copy the code

Arrow function and this

Let’s start with an example:

const awardsInfo = {
  name: 'Chen Lingshi',
  age: '10',
  prize: 'Third prize'.takePart() {
    return function(){console.log(' name:${this.name}Age:${this.age}, participated in the National Youth Science and Technology Innovation Competition and won${this.prize}}}} const awards=awardsInfo. TakePart () // 标 签 : awardsInfo takePart() Undefined, won undefined Awards in National Youth Science and Technology Innovation Competition ()Copy the code

As you can see, the expected result here is not the same as the actual result. The reason for this is simple: Awards runs in a global scope, and when called, this in Awards is window, which has no name, age, or prize attributes.

Knowing why we can solve this problem well, we can use the arrow function of ES6:

takePart() {
    return() => {console.log(' name:${this.name}Age:${this.age}, participated in the National Youth Science and Technology Innovation Competition and won${this.prize}`)}}Copy the code

The arrow function has several uses with caution.

(1) The this object inside the function is the object at which it is defined, not the object at which it is used.

(2) Cannot be used as a constructor, that is, cannot use the new command, otherwise an error will be thrown.

(3) You can’t use arguments. This object does not exist in the function body. If you do, use the REST argument instead.

(4) Yield cannot be used, so arrow functions cannot be used as Generator functions.

For more on ES6 arrow functions see Portal: Arrow Functions

This parameter

There is now a Superman named Zenino, which defines two interfaces: SuperPowers and SuperHuman. The code looks like this:

interface SuperPowers {
  name: string,
  describe: string;
  toString: () => string;
}

interface SuperHuman {
  name: string;
  sex: 'male' | 'woman';
  superPowers: Array<SuperPowers>;
  introduce: () => Function
}

const CenYinuo: SuperHuman = {
  name: 'Zen Ono',
  sex: 'woman',
  superPowers: [
    {
      name: '300 words per day',
      describe: 'Using a literature robot developed by myself, li Qingzhao was ashamed to see the words he wrote in ten minutes and then made after ten minutes.' +
        'Xin Qiji saw the direct abandoned text, Wang Anshi saw the direct worship, Su Shi saw the direct withdrawal from the Tang and Song eight everyone's group'.toString() {
        return `${this.name}:${this.describe}`
      }
    },
    {
      name: 'Two thousand poems per day',
      describe: 'Use the literature robot developed by myself to write poems quickly within half an hour. After half an hour, Li Bai gave up the title of poem fairy when he saw the beginning, and Du Fu felt ashamed after listening to his poem.'.toString() {
        return `${this.name}:${this.describe}`
      }
    }
  ],
  introduce(): Function {
    return() => {const power1 = this.superpowers [0] const power2 = this.superpowers [1] console.log(' name:${this.name}\ n gender${this.sex}\n Superpowers :\n\ T1.${power1.toString()},\n\t2.${power2.toString()}`)}}} const introduce = CenYinuo. Introduce introduce () () / / name: cen a gender female / / / / abilities: / /...Copy the code

While the code works (there’s nothing wrong with the code itself), there’s one problem: TypeScript doesn’t correctly infer the types of arrays in it

That’s fine, but it’s not as comfortable to develop: If superPowers has a lot of complex types, it can be tough to develop (and the TS editor hints are really good, I have to say). Now that you see the problem, how do you solve it?

Then deal with the person who asked the question!

Laozi’s backhand is a TM quadruple:

We can solve this problem simply by telling TypeScript what type this is in this function. Yes, this parameter

When you pass a function into a library function that will be called later. Because when callbacks are called, they are treated as a normal function call, this will be undefined. With a few tweaks, you can use this to avoid errors.


First, the author of the library function specifies the type of this;

The function is then called so that TypeScript can detect the type of this.

We just need the interfaceSuperHumanMake a little change

interface SuperHuman {
  name: string;
  sex: 'male' | 'woman';
  superPowers: Array<SuperPowers>;
  introduce: (this:SuperHuman) => Function
}Copy the code

We can see that TypeScript already detects the parsed type in the array:


Function overloading

What is function overloading? Here’s a quote from Wikipedia:

Function overloading (English: function overloading) is a feature of Ada, C++, C#, D, and Java programming languages. This feature allows the creation of several subroutines with the same name but different input and output types or numbers. It can simply be called the ability of a single function to perform multiple tasks.

In TypeScript, we can define different parameters for functions to return different types, as in the following example:

function reverse(val: number): number
function reverse(val: string): string
function reverse(val: Array<any>): Array<any>
function reverse(val: number | string | Array<any>): number | string | Array<any> {
  if (typeof val === 'number') {
    return +val.toString().split(' ').reverse().join(' ')}else if (typeof val === 'string') {
    return val.toString().split(' ').reverse().join(' ')}else {
    return val.reverse()
  }
}

console.log(reverse(2020))
console.log(reverse('hello word! ')) to the console. The log (reverse ([1, 2, 3, 4])) / / output results: / / 202 / /! drow olleh // [ 4, 3, 2, 1 ]Copy the code

Doesn’t it seem amazing that TypeScript type checking works? Let’s look at the types of the above three functions:


The reverse(2020) detection type is function reverse(val: number): number:

reverse(‘hello word! Function reverse(val: string): string:

Function reverse(val: Array

): Array

:

This example already shows the benefits of overloading for functions with complex logic

class

In object-oriented programming, a class (English: class) is a construct of an object-oriented computer programming language. It is a blueprint for creating objects, describing the common properties and methods of the objects created.

A more rigorous definition of a class is a cohesive package made up of a particular kind of metadata. It describes the behavior rules of objects, which are called instances of the class. Classes have interfaces and structures. Interfaces describe how methods interoperate with classes and their instances, while structures describe how data in an instance is divided into properties. A class is the most specific type of object associated with a layer. Classes can also have runtime representations (meta-objects) that provide runtime support for manipulating class-related metadata.

Before ES6, classes are not like classes, but like functions, because before ES6, classes are implemented with constructors:

function Point(x, y) {
  this.x = x;
  this.y = y;
}

Point.prototype.toString = function () {
  return '(' + this.x + ', ' + this.y + ') ';
};

var p = new Point(1, 2);Copy the code

Compared to traditional object-oriented languages (Java, C++, C#, etc.), the implementation of JavaScript classes is “weird”, perhaps their inner OS is like this:

This article assumes that you already know the Class of ES6+ (if you don’t, please enter the portal: As an extension to JavaScript, TypeScript extends the ES6+ Class syntax to make it more flexible and “object-oriented.” We’re just talking about TypeScript extensions to class.


Syntactic sugar (English: Syntactic sugar) is a term coined by British computer scientist Peter Landin for a type of grammar added to a computer language that has no effect on how the language functions, but is more convenient for programmers to use. Syntax sugar makes the program more concise and more readable.


The modifier

TypeScript extends three modifiers for Class:

  • Public: The modified property or method is public and can be accessed anywhere. This is the default value of the property or method
  • Private: Indicates that a property or method is private and cannot be accessed from outside the class. If accessed from outside, an error is reported (but can be compiled).
  • Protected: Make properties and methods protected and accessible within a class, but only within a subclass.

These three modifiers are very common in traditional object-oriented languages and will be familiar to you if you’ve studied Java, C++, etc., but if you’re going straight to JavaScript, they may take a little getting used to.

class Animal {
  private className = 'Animal'
  protected des = 'This is an animal.'
  public species: string

  constructor(species: string) {
    this.species = species
  }
}

class Dog extends Animal {
  name: string

  constructor(varieties: string, name: string) {
    super('dog')
    console.log(this.species)
    console.log(this.des)
    this.name = name
  }
}

const dog = new Dog('Chinese Rural Dog'.'dog')
const animal=new Animal('the horse')
console.log(animal.className)//Property 'className' is private and only accessible within class 'Animal'.
console.log(animal.des)//Property 'des' is protected and only accessible within class 'Animal' and its subclasses.
dog.des//Property 'des' is protected and only accessible within class 'Animal' and its subclasses.Copy the code

Note:

When using the private modifier constructor, the class is not allowed to be initialized or inherited (compilation will still pass, but TypeScript will report an error) :

class Animal {
  private constructor() {
  }
}

//Cannot extend a class 'Animal'. Class constructor is marked as private.
class Dog extends Animal {
  constructor() {
    super()
  }
}

const dog = new Dog()
const animal = new Animal()//Constructor of class 'Animal' is private and only accessible within the class declaration.Copy the code

When protected modifier constructors are used, they can only be inherited:

class Animal {
  protected constructor() {
  }
}

class Dog extends Animal {
  constructor() {
    super()
  }
}

const dog = new Dog()
const animal = new Animal()//Constructor of class 'Animal' is private and only accessible within the class declaration.Copy the code


Parameter properties

Static properties (public)

Public modifies the parameter to define and assign a value to it, making code more concise:

class Calendar extends Date {
  constructor(public year: number, public month: number) {
    super()
  }
}

const c: Calendar = new Calendar(2020, 7)
console.log(c.year)
console.log(c.month)Copy the code

The above code is equivalent to the following code:

class Calendar extends Date { year:number month:number constructor(year:number,month:number) { super() this.year=year This. Month =month}} const c: Calendar = new Calendar(2020,7) console.log(c.ear) console.log(c.month)Copy the code


Read-only properties (readonly)

You can use the readonly modifier when you want a value not to be assigned at any time except when it is initialized. When it is present with other modifiers, it should be positioned after the modifier:

class Calendar extends Date {
  constructor(public readonly year: number) {
    super()
  }
}

const c: Calendar = new Calendar(2020)
c.year = 2019//Attempt to assign to const or readonly variable Copy the code


An abstract class

Abstract class is also a common concept in traditional object-oriented programming languages. The following is the introduction of abstract class in Baidu Encyclopedia:

Image class is often used to represent the abstract concepts derived from the analysis and design of the problem domain. It is the abstraction of a series of concrete concepts that look different but are essentially the same.

Classes that are usually decorated with abstract in programming statements are abstract classes. In C++, classes that contain purely virtual functions are called abstract classes and cannot generate objects. In Java, classes that contain abstract methods are called abstract classes and also cannot generate objects.

In TypeScript, abstract classes cannot be instantiated. They can only be implemented or inherited. In addition, attributes and methods can also be modified:

abstract class MobileDevices {
  abstract monitor: string
  abstract CPU: string
  abstract GPU: string
  abstract RAM: string

  abstract powerOn()
}

class Phone extends MobileDevices {//

  constructor(
    public readonly CPU,
    public readonly GPU,
    public readonly RAM,
    public readonly monitor,
  ) {
    super()
  }

  powerOn() {
    console.log('Press the power button, and the music starts slowly. Two minutes later... Startup successful ')
  }

}

const md = new MobileDevices()//Cannot create an instance of an abstract class.
const nokiaElderlyPhone = new Phone('ARM11'.'no'.'512kb'.'2 inch large display screen ')
nokiaElderlyPhone.powerOn()Copy the code

Or phone class to implement MobileDevices:

class Phone implements MobileDevices {

  constructor(
    public readonly CPU,
    public readonly GPU,
    public readonly RAM,
    public readonly monitor,
  ) {
  }

  powerOn() {
    console.log('Press the power button, and the music starts slowly. Two minutes later... Startup successful ')}}Copy the code


Class as type

In addition to allowing subclasses to inherit, implement, and instantiate, class definitions can also be used as types in a similar way to interfaces:

const md: MobileDevices = {
  CPU: ' ',
  GPU: ' ',
  RAM: ' ',
  monitor: ' '.powerOn() {}} // const nokiaElderlyPhone: Phone = {CPU:' ',
  GPU: ' ',
  RAM: ' ',
  monitor: ' ',
  powerOn(): any {
  }
}Copy the code

Classes and interfaces

Now that we’ve looked at the basic uses of Class, let’s look at the special uses between interfaces and classes.

Interface inheritance class

As mentioned earlier, interfaces can inherit from interfaces, but you’d never expect interfaces to inherit from classes (thanks to the flexibility of JavaScript).

Class Glass {Capacity: string Shape: string size: string Load (food: string) {console.log(' load${food}`)
  }
}

interface MugGlass extends Glass {
  CupHandleShape: string
}

const mugGlass: MugGlass = {
  CupHandleShape: ' ',
  capacity: ' ',
  shape: ' ',
  size: ' ',
  load(food: string): void {
  }
}Copy the code


Class implementation interface

If a class implements a class, can a class implement an interface? The answer is yes, and multiple interfaces can be implemented simultaneously

interface GlassInterface {
  capacity: string
  shape: string
  size: string
  load: (food: string) => void
}

interface Structure {
  bodyShape: string;
  lidShape: string;
}

class MugGlass implements GlassInterface, Structure {
  bodyShape: string
  lidShape: string
  capacity: string
  shape: string
  size: string
  CupHandleShape: string

  load(food: string): void {
  }
}

const mugGlass: MugGlass = {
  bodyShape: ' ',
  lidShape: ' ',
  CupHandleShape: ' ',
  capacity: ' ',
  shape: ' ',
  size: ' ',
  load(food: string): void {
  }
}Copy the code


The generic

introduce

What is a generic? The following paragraph is taken from Generics in Wikipedia

Generic programming is a style or paradigm of programming languages. Generics allow programmers to write code in strongly typed programming languages using types that are specified later and specified as parameters when instantiated. Support for generics varies from programming language to programming language and from compiler to runtime environment. Ada, Delphi, Eiffel, Java, C#, F#, Swift and Visual Basic.net call it generics; ML, Scala, and Haskell call it parametric polymorphism; C++ and D are called templates. The influential 1994 book Design Patterns called it parameterized Types.


TypeScript is strongly typed, but it also inherits the flexibility of JavaScript, such as any and generics in this chapter. Let’s look at a simple example of generics:

function createArray<T>(length: number, defaults: any): Array<T> {
  const arr: Array<T> = []
  for (let i = 0; i < length; i++) {
    arr.push(defaults)
  }
  return arr
}

const numArr = createArray<number>(20, 0)Copy the code

In the code above, we specify T as type number when calling a method (T is just a type variable and can be replaced with another letter, a single capital letter definition is recommended). We can use type T in function scope, and TypeScript will infer the type of numArr from the generic type specified on the call:

It is also possible to define more than one generic type, as in the following example:

function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]];
}

swap([7, 'seven']); / / /'seven'And 7)Copy the code


Constrain generic types

While generics are flexible, TypeScript doesn’t automatically infer types because it doesn’t specify specific types. In this case, we can specify what properties generics have:

async function god2BaiduAsync<T extends number>(gdLon: T, gdLat: T, wait = 0): Promise<[T, T]> {
  return new Promise(resolve => {
    setTimeout(() => {
      let bdLatLon: [T, T] = [gdLon, gdLat]
      letPI = 3.14159265358979324 * 3000.0/180.0let x = gdLon
      let y = gdLat
      letSQRT (x * x + y * y) + 0.00002 * math.sin (y * PI)letTheta = math.atan2 (y, x) + 0.000003 * math. cos(x * PI) bdLatLon[0] = <T>(z * math. cos(theta) + 0.0065)// Generic type predicate, BdLatLon [1] = <T>(z * math.sin (theta) + 0.006) resolve(bdLatLon)},wait)
  })
}

//Argument of type '" 113.97471 "' is not assignable to parameter of type 'number'.
const baiduPosition = god2BaiduAsync<number>('113.97471'.'22.660848') const baidu_list position2 = god2baidu_list async <number>(113.97471, 22.660848)Copy the code

Generics are defined as number. When called, an error will be reported if the type is not number. Generics are also used for type assertion in some cases (type assertion will be covered later).

A generic interface

We wrote one before

createArray

Function, what if we use interface + generics to define its shape:

interface CreateArray {
  <T>(length: number, defaults: any): Array<T>
}

const createArray: CreateArray = <T>(length: number, defaults: any): Array<T> => {
  const arr: Array<T> = []
  for (let i = 0; i < length; i++) {
    arr.push(defaults)
  }
  return arr
}

const numArr = createArray<number>(20, 0)Copy the code

We can optimize CreateArray a little bit and promote generics to the interface name:

interface CreateArray<T> {
  (length: number, defaults: T): Array<T>
}

const createArray: CreateArray<any> = <T>(length: number, defaults: any): Array<T> => {
  const arr: Array<T> = []
  for (let i = 0; i < length; i++) {
    arr.push(defaults)
  }
  return arr
}

const numArr = createArray(20, 0)Copy the code

In the last article on arrays, we explained that defining an array is a generic way to define an array. Now we simply implement a generic array by hand:

interface PseudoArray<T> {
  [index: number]: T
}

let numArr: PseudoArray<number> = [1, 3]
let strNum: PseudoArray<string> = new Array('1'.'2'.'3')
letBolArr: PseudoArray < Boolean > = [1, 3] / / Type'number' is not assignable to type 'boolean'.Copy the code

A generic class

Generics can be used in interfaces as well as in classes. Let’s use class to implement the above generic array

class PseudoArray<T> { [index: number]: T constructor(... args) {return args
  }
}

let numArr: PseudoArray<number> = [1, 3]//[ 1, 3 ]
let strArr: PseudoArray<string> = new PseudoArray<string>('1'.'2'.'3') / / /'1'.'2'.'3' ]
let bolArr: PseudoArray<boolean> = new PseudoArray<string>('1'.'2'.'3')//Type 'PseudoArray<string>' is not assignable to type 'PseudoArray<boolean>'.   Type 'string' is not assignable to type 'boolean'.Copy the code

Default values for generic parameters

We can set the default type for generics:

function createArray<T = number>(length: number, defaults: any): Array<T> {
  const arr: Array<T> = []
  for (let i = 0; i < length; i++) {
    arr.push(defaults)
  }
  return arr
}Copy the code

Type inference is compatible with types

Type inference

What is type inference?

What about TypeScript type inference? TypeScript type inference is familiar, but let’s take a look at how TypeScript type inference works.

TypeScript type inference, TypeScript actually implements type inference inside TypeScript, and you might be surprised at how TypeScript does type inference. But that’s what happened, and xiaobian was very surprised.

That’s all about TypeScript type inference. Let me know what you think in the comments!

All right, enough. Put down your beer bottle, brick, mace, and let me talk

In TypeScript, if a type is not defined, it is automatically inferred from what is assigned. For example:

let num=1
num='1'//Type '" 1" is not assignable to type 'number'.Copy the code

Variables that are not assigned are automatically inferred to be of type any:

let num
num = 1
num = '1'
num = false
num = trueCopy the code


Best generic type

When there are multiple types, TypeScript uses them to infer the most appropriate type:

let arr = [1, 2, true.'3']Copy the code

If your editor is typescript-friendly, hovering over a type will prompt the result of type inference:

As you can clearly see, TypeScript extrapolates to a combined type that contains all types

Because all types are chosen as candidates, there may be deviations when using subtypes that inherit from the same parent type:

interface Man extends Human {
  
}
interface Woman extends Human {
  
}

const epson: Man = {
}

const marry: Woman = {
}
const peoples=[epson,marry]Copy the code

Array

:

At this point we can manually specify its parent type as the most appropriate type:

const peoples:Array<Human>=[epson,marry]Copy the code


Type is compatible

TypeScript type compatibility is based on structural subtypes and does not require explicit declarations.


A structural type is a way to describe a type using only its members, in contrast to a nominal type.

Nominal types: compatibility or equivalence of data types is determined by an explicit declaration and/or the name of the type. Nominal data type languages (c#, Java)

Compatibility between objects

Compatibility between object is simple, if there are two objects: a, b, a to be compatible with b object, is at least the same properties with a b (the same name and type, is a more additional attributes), such as: animal compatible with human (animal contains the human), but humans can’t compatible with animals (can’t say human contains animal).

interface Computer {
  GPU: string;
  CPU: string;
  RAM: string;
}

interface Phone {
  GPU: string;
  CPU: string;
  RAM: string;
  screen: string;
}

let myComputer:Computer={
  CPU: 'Thread ripper',
  GPU: 'RTX2080Ti',
  RAM: '1T'
}

let myPhone:Phone={
  CPU: Furthermore, '1000',
  GPU: 'no',
  RAM: '16G',
  screen: 'samsung'
}

myComputer=myPhoneCopy the code


Compatibility between functions

Functions are not compatible with objects. For example, if x can be assigned to y, the x argument types must appear in the argument list of y in order:

let x = (n: number) => n
let y = (a: number, b: string) => a
y = x
x=y//Type '(a: number, b: string) => number' is not assignable to type '(n: number) => number'.Copy the code

Where y has two arguments and x has one, TypeScript allows you to ignore the rest, following JavaScript conventions such as array. filter(Element :any,index? :number,array? :Array

), where only element is required and other parameters can be ignored


For the return value, it follows the object compatibility rules. For example, the return value of Y must be compatible with the return value of X, and the return value of X must contain at least the attributes of the return value of Y:

let x: () => { n: number, s: string } = () => ({n: 1, s: ' '})
let y: () => { n: number } = () => ({n: 1})
y = x
x = y// Type '() => { n: number; } ' is not assignable to type '() => { n: number; s: string; } '.   Property 's' is missing in type '{ n: number; } ' but required in type '{ n: number; s: string; } '.Copy the code


The enumeration

Different enumerations are not compatible even if they have the same value; But enumeration types and numeric types are compatible with each other:

enum Duirection {
  right,
  left,
  bottom,
  top
}

enum Color {
  red,
  green,
  blue,
  none
}

enum MobileDevices {
  phone = 'phone',
  computer = 'computer',
  watch = 'watch'
}

let d = Duirection.top
d = 1
d = Color.red//Type 'Color.red' is not assignable to type 'Duirection'.

let md = MobileDevices.phone
md = 'computer'//Type '"computer"' is not assignable to type 'MobileDevices'.

const num: number = Duirection.topCopy the code


class

Classes are almost as compatible with literals, with one difference: classes have static and instance part types.

When comparing two class type variables, only instance members are compared; static members and constructors are excluded from the comparison.

class Animal {
  feet: number;
  constructor(name: string, numFeet: number) { }
}

class Size {
  feet: number;
  constructor(numFeet: number) {
  }
}

let a: Animal;
let s: Size;

a = s;  // OK
s = a;  // OKCopy the code


High-level types

The types described above are sufficient for development purposes, but there are also advanced types that are used less often but may be needed in some situations

Cross type

A crossover type sounds like an intersection of several types, but it is actually a union of several types. Scenarios of crossover types: Mixins, etc

interface AnyObj {
  [pro: string]: any
}

function extend<F extends AnyObj, S extends AnyObj>(first: F, second: S): F & S {
  const result: Partial<F & S> = Object.assign({}, first, second)
  return <F & S>result
}

interface Person {
  name: string,
  age: number
}

interface Ordered {
  serialNo: number,

  getSerialNo(): number
}

const personA: Person = {
  name: 'Jim',
  age: 20
}

const orderOne: Ordered = {
  serialNo: 1,
  getSerialNo() {
    return this.serialNo
  }
}
const personOrderd = extend(personA, orderOne)Copy the code

The joint type

The union type specifies that the data may be one of several types.

Associative types are common in practical applications, such as an object whose value may be a tuple or the type itself:

interface InfoData {
  [name: string]: {
    [name: string]: [string, string] | InfoData
  }
}Copy the code


Type the alias

A type alias is used to give a type a new name. It can be a primitive value, primitive data type, union type, tuple, crossover type, and any other type that needs to be written by hand:


Define a liger interface:

interface Lion {
  family: string;
  color: string;
  maneColor: string;
  status: string;
  age: number;
}

interface Tiger {
  stripe: string;
  swimmingSpeed: number;
  treeClimbingSpeed: number;
}

type Liger = Lion & TigerCopy the code


Prompt type:

type MessageType = 'success' | 'info' | 'warning' | 'error'Copy the code


Or a union of intersecting types:

type MessageType = 'success' | 'info' | 'warning' | 'error'
type UserRole = 'admin' | 'master' | 'tourist'
type types = MessageType | UserRoleCopy the code

Types of assertions

Type units can be used in two ways

1. Use as assertions

Value as typeCopy the code

2. Generic assertions

< type > valueCopy the code


Here’s an example:

interface Cat {
  name: string;

  climbing(): void
}

interface Fish {
  name: string;

  swim(): void
}

function isCat(animal: Cat | Fish) {
  //Property 'climbing' does not exist on type 'Cat | Fish'.   Property 'climbing' does not exist on type 'Fish'.
  if(animal? .climbing ??false) {
    return true
  }
  return false
}

const fish: Fish = {
  name: ' ',
  swim(): void {
  }
}
console.log(isCat(fish))Copy the code

You can see in the isCat method animal? To resolve this problem, we can assert that animal is Cat or Fish:

function isCat(animal: Cat | Fish) {
  if((animal as Cat)? .climbing ??false) {
    return true
  }
  return false
}Copy the code

Use of assertions

  • Assert the union type as one of these types
  • The parent class is asserted as a more concrete subclass
  • The any assertion is of a specific type

A statement to merge

TypeScript consolidates declarations of functions, classes, and interfaces with the same name into a single function, class, and interface

interface

Without further ado, let’s get straight to the example:

interface Book {
  long: string;
  wide: string;
  thickness: string;
}

interface Book {
  pageNumber: number;
  author: string;
  press: string;
}

const book: Book = {
  author: ' ',
  long: ' ',
  pageNumber: 0,
  press: ' ',
  thickness: ' ',
  wide: ' '
}Copy the code

As you can see, although both Book interfaces are defined, no errors are reported, and when an object is declared as a Book type, the object has all the properties of both Book interfaces.

function

Function merge reference function overloading

Declaration file

introduce

Declaration files are often used for custom functionality (such as custom global utility classes, extension framework functionality, etc.) in third-party libraries and projects that do not provide type declarations. The coded hint experience provided by type declarations is one of the things that makes TypeScript so popular.


As non-library developers, we only need to learn how to declare global variables, extension frameworks, and extension libraries.


Before we get to the learning declaration file, we need to understand two concepts:

  • Declarative statements
  • Declaration file

What is a declaration statement?

Declarative statements are simply declarations of variables, functions, and types that TypeScript recognizes.

For example, with JQuery as a CDN, to get TypeScript to recognize $, we could write:

declare function $(selector: string): $Copy the code

Declare a global function $whose selector is of type string, and then return $itself.

What is a declaration file?

The declaration file contains many declarations with the suffix.d.ts. The declaration file is used to encode hints and define types and is deleted at compile time.

Taking JQuery as an example, TypeScript doesn’t know JQuery exists if we introduce it as a CDN. There are two options:

  1. Download the third party declaration document provided by the community (@typeFile)
  2. Handwritten declaration document

Create a new JQuery.d.ts file that uses its selector function and hide function:

// @ts-ignore
declare function $(selector: string): $

declare namespace $ {
  functionhide(duration? : number | string, complete? : Function)functionhide(option: { duration? : number | string, easing? : string, queue? : boolean | string, specialEasing? : object, setp? : Function, progress? : Function, complete? : Function,done? : Function, fail? : Function, always? : Function }) }Copy the code

TypeScript uses the.hide method normally

const inner = $('#inner')
setTimeout(() => {
  inner.hide()
}, 2000)Copy the code

Declare global variables

Global variables are defined by declare. If the export and import keywords are found in the declaration file, they are considered as UMD package lifeforms and are changed from global to local (valid only in the current file scope).

The global variable

Global variables can be declared var, declare let, declare const to define global variables and constants.

Declare var and Declare let define global variables. There is no difference in theory, but we use ES6+ in our project, so we recommend using declare let definition:

declare let JQuery:(selector:string)=>anyCopy the code

Declare const is used to define global constants, which cannot be changed. It is recommended to declare const rather than declare let.

declare const JQuery:(selector:string)=>anyCopy the code

The global method

Use declare function to declare a global function that can be overloaded, as in JQuery’s $definition:

// @ts-ignore
declare function $(): $
// @ts-ignore
declare function $(element: Element): $
// @ts-ignore
declare function $(object: Object): $
// @ts-ignore
declare function $(selector: string, content: Element | $): $Copy the code

The code prompt looks like this:

Global class

When a variable is a global class, it can be defined using a DECLARE class, such as when importing a router through a CDN:

declare class VueRouter {

}Copy the code

Global type

Global types can be defined directly with interface or type:

//index.d.ts
interface Pen {
  size: string;
}

//index.ts
const pen: Pen = {
  size: ' '
}Copy the code

Global enumeration

Use declare Enum to define global enumerations (also known as external enumerations, which are used only to define types and do not compile the actual content) :

//index.d.ts
declare enum Direction {
  up,
  right,
  down,
  left
}

//index.ts
console.log(Direction.up)// ReferenceError: Direction is not definedCopy the code

Global object

Use declare namespace to define global variables (namespaces) and declare them directly with let, const, enum, interface, function, etc., as in JQuery:

declare namespace $ {
  const jquery:string
  functionhide(duration? : number | string, complete? : Function)functionhide(option: { duration? : number | string, easing? : string, queue? : boolean | string, specialEasing? : object, setp? : Function, progress? : Function, complete? : Function,done? : Function, fail? : Function, always? : Function }) }Copy the code

You can also declare objects internally:

declare namespace $ {
  const jquery:string
  namespace fn{
    const a:string
  }
}Copy the code

Extend global variable

In everyday projects, we might need to extend global variables, such as adding a refresh method to the window object that calls location.reload() :

<script>
  window.refresh = () => {
    location.reload()
  }
</script>Copy the code

We declare that the Window object can be extended with interface:

//index.d.ts
interface Window {
  refresh(): void
}
//index.ts
setTimeout(()=>{
  window.refresh()
},2000)Copy the code

Or add the refresh method declaration to the global namespace with declare namespace:

//index.d.ts
declare namespace Global {
  function refresh(): void
}
//index.ts
setTimeout(()=>{
  refresh()
},2000)Copy the code


Extending existing modules

The syntax of the extension module is Declare Module


For example, in the Vue project, we used the CSS Module and defined a global plugin Toast. We added a method $Toast to the Vue instance. We could write the declaration file like this:

// Extend vue/types/vue.d.ts to expose modulesdeclare module 'vue/types/vue' {
  interface Vue {
    $style: {
      [key: string]: string;
    };
  }

  interface VueConstructor {
    $style: {
      [key: string]: string;
    };
    $toast(option: Option): void; }}Copy the code

conclusion

This chapter may be a little difficult for students who have not been exposed to strongly typed languages such as Java and C++. These things are also very common in real projects. It is recommended to do more small examples.


This article may not be comprehensive, incorrect or difficult to understand, feel free to comment in the comments section 😊😊

In the meantime, everyone, spray gently, after all:


The next article will look at how TypeScript is used in projects. I’m a figurehead