Hello everyone, I am a bowl week, a front end that does not want to be drunk (inrolled). If I am lucky enough to write an article that you like, I am very lucky

Writing in the front

TypeScript is now a must-have skill for almost every front-end programmer, and TS is now used in every framework. This article summarizes the common knowledge points in TS, hoping to help you.

Type a,

1. Basic types

The basic types in TypeScript include Boolean, number, string, Any, Void, Null, Undefined, and Never. The following code shows the definition of TS basic types:

; (function () {
  /* * In TS, the data type can be defined by the let variable name: data type = variable value value (type annotation) * or it can be defined without specifying the data type, and TS itself will infer the data type */
  / / the Boolean
  let boo: boolean = false // Assigning a non-boolean value throws an exception

  // Number type
  let num: number = 100

  / / string
  let str: string = 'String' // Use single or double quotation marks
  str = 'Template string' // Use the template string definition

  // Any type -> indicates that this type can be a dynamic type, which removes type checking at compile time
  let AnyType: any = 123
  AnyType = true // Repeated assignments do not throw an exception

  // Void type -> usually used for function types that have no return value
  function demo() :void {
    console.log('Test void type')
  }
  demo()

  // There are two special types: null and undefined
  // These two types are subtypes of all types, which means they can be assigned to number, string, etc
  let u: undefined = undefined
  num = u // Assign undefined to a variable of type number
  let n: null = null
  boo = n // Assign a Boolean variable to null}) ()Copy the code

The base type is relatively simple, particularly similar to JavaScript, which simply has one more type definition than JavaScript

TS also has a Never type. This type represents worthy types that will never exist. For example, the never type is the return type of function expressions or arrow function expressions that always throw an exception or have no return value at all.

2. An array oftype

Arrays in TypeScript are different from arrays in JavaScript. Arrays in TS not only define a variable as an array, but also locate the types in the array. Example code is as follows:

; (function () {
  // Define an array type that is just a number
  let arr1: number[] = [1.2.3]
  console.log(arr1)
  // Define an array that can be Boolean values of numeric strings
  let arr2: (number | string | boolean)[] = ['1'.'2'.true]
  console.log(arr2)
  // Define an array of any type
  let arr3 = [1['1'.'2'.true].true]
  console.log(arr3)

  // Define an array of object types. The object must have name and age attributes
  const objectArray: { name: string; age: number }[] = [
    { name: 'Bowl Week'.age: 18},]// Or declare it as a type alias
  // Define a type alias through type
  type User = { name: string; age: number }
  const objectArr: User[] = [{ name: 'Bowl Week'.age: 18 }]
})()
Copy the code

3. Tuple type

The tuple type allows you to represent an array with a known number and type of elements that need not be of the same type. The sample code looks like this:

; (function () {
  // Define a tuple of string and number
  let tuple: [string.number] = ['123'.123]
  console.log(tuple) // ['123', 123]
  // Assign by index
  tuple[0] = 'string'
  console.log(tuple) // [ 'string', 123 ]
  // Assign other types
  // tuple[0] = true
  // console.log(tuple) // Throws an exception}) ()Copy the code

The main purpose of a tuple is to constrain each item in an array and its length.

Tuples and arrays can be nested as follows:

// Nesting of tuples and arrays
let tuples: [string.number[[] [] ='123'.123],
    ['456'.456]]Copy the code

In the above code, [string, number] represents a tuple, followed by [], which represents an array of tuples.

4. Object type

All of the above types can be contained in one object, as shown in the following example code:

; (function () {
  // Define an object containing two attributes, MyName and age, where MyName is string and age is number
  let obj: {
    MyName: string
    age: number
  }
  // An assignment to an object that throws an exception if it is not assigned to the type specified above
  obj = {
    MyName: 'Bowl Week'.age: 18,}console.log(obj) // {MyName: '1 ', age: 18}}) ()Copy the code

We don’t need to annotate types everywhere in TS, because type inference can help us get their functionality without writing extra code. But if you want to make your code more readable, you can write each type.

5. Type inference

Sometimes in TypeScript you don’t need to specify a type explicitly, and the compiler will automatically infer the appropriate type, as in this code:

; (function () {
  let myName = 'Bowl Week'
  myName = true // Error: Cannot assign type "Boolean" to type "string"}) ()Copy the code

When we define the myName variable, we do not specify its data type. We just assign it a string value, but if we reassign the value to a non-string value, the compiler will throw an exception.

This is TypeScript’s simplest type inference, where you infer the data type of a variable from the value on the right.

6. Type assertion

A type assertion is when you tell TS that the worth data type is something and you don’t need to check it.

This way, it does not affect the runtime, only at compile time. Example code is as follows:

let SomeValue: any = 'this is a string'
/ / a grammar
let StrLength1: number = (<string>SomeValue).length
// Syntax 2 as syntax
let StrLength2: number = (SomeValue as string).length

Copy the code

It is worth noting that only the second syntax is supported when using JSX in TS.

7. Enumeration types

An enumerated type gives a name to a set of values.

Enum types are common in C++ and Java languages. TypeScript adds enum types to JavaScript types.

For example, if we need to define a set of roles that need to be represented by numbers, we can use the following code to locate them:

enum role{
    STUDENT,
    TEACHER,
    ADMIN
}
Copy the code

In the above code, we define role as an enumerated type, which contains three values. TypeScript automatically assigns an ordinal number to each value, starting at 0 by default, with values 0, 1, 2.

Of course, we can also customize each value, if not all of the values will be increased based on the previous value. Example code is as follows:

enum role1 {
    student = 1.// The next two values are 2 and 3
    teacher,
    admin,
}
enum role2 {
    // Each name has a specified value
    student = 1,
    teacher = 3,
    admin = 6,}Copy the code

8. Union types

The so-called joint types is to define some types, variables defined you just need to meet any one type of joint types using | definition, the example code is as follows:

/ / by | symbol definition joint type
let value: number | boolean | string = 'Bowl Week'
value = 18

Copy the code

In the above code we define a value variable, which can be of type number, Boolean, or String.

9. Cross types

Union types were introduced, followed by cross types that are particularly similar to them.

A cross type needs to satisfy all types. A cross type is defined using the ampersand symbol. Example code is as follows:

// Define three common interface types
interface Name {
  name: string
}
interface Age {
  age: number
}
interface Hobby {
  hobby: string
}
// Define an object that is the combined type of the above three objects
let person: Name & Age & Hobby = {
  // An exception will be thrown if one type is not allocated
  name: 'Bowl Week'.age: 18.hobby: 'coding',}Copy the code

10. Type protection

Now we have a requirement to get the first number in an array of any type.

The implementation code is as follows:

// Define an array containing either number or string
const arr: (number | string) [] = [1.'digital']
// Returns the first number
const getValue: (arr: (number | string) []) = > number = (
  arr: (number | string) [],) :number= > {
  for (let i = 0; i < arr.length; i++) {
    // If the current value is not a NaN when converted to a number
    if (!isNaN(Number(arr[i]))) {
      return arr[i] / / can't type "string | number" assigned to type "number".}}}Copy the code

We don’t know if we’re returning a number. So an exception will be thrown.

This can be done with type assertion, as shown in the following code:

const getValue: (arr: (number | string) []) = > number = (
  arr: (number | string) [],) :number= > {
  for (let i = 0; i < arr.length; i++) {
    // If the current value is not a NaN when converted to a number
    if (!isNaN(Number(arr[i]))) {
      return arr[i] as number // Tell the compiler that I am returning a number}}}Copy the code

Using type assertions can be cumbersome if you want different data types. In this case, type protection is required to complete the function. Type protection can be divided into the following three types:

  • Custom type protection
  • typeofType of protection
  • instanceofType of protection

Custom type protection

You can customize type protection by defining a function whose return structure is a parameterName is type, which is a type predicate. ParameterName must be a parameterName from the current function parameter. Example code is as follows:

// Use custom type protection
ParameterName is Type. ParameterName is the Type of the parameterName is
function isNumber(value: number | string) :value is number {
  // If true is returned, the value passed is the type after is
  return !isNaN(Number(value))
}
// 2. Define a function to get a number
const getNumber: (value: number | string) = > number = (
  value: number | string,
): number= > {
  // If isNumber is true, value is a number, so return the number
  if (isNumber(value)) {
    return value
  }
}
// 3. Call to get the final value
const getValue: (arr: (number | string) []) = > number = (
  arr: (number | string) [],) :number= > {
  for (let i = 0; i < arr.length; i++) {
    // If a number is returned, convert to Boolean with the value true
    if (getNumber(arr[i]) || getNumber(arr[i]) === 0) {
      return getNumber(arr[i])
    }
  }
}

Copy the code

The reason for defining the second function is that using I directly as a return value in an array is still problematic, so define a function to transition.

typeofType of protection

The typeof keyword in JavaScript can determine the current type, but only number, String, Boolean, and symbol.

That’s enough for this requirement, so let’s look at how to implement type protection through Typeof. Example code is as follows:

// 1. Define a function to get a number
const getNumber: (value: number | string) = > number = (
  value: number | string,
): number= > {
  // Determine whether the current is a string, if so return the current value
  if (typeof value === 'number') {
    return value
  }
}
// 2. Call to get the final value
const getValue: (arr: (number | string) []) = > number = (
  arr: (number | string) [],) :number= > {
  for (let i = 0; i < arr.length; i++) {
    // If a number is returned, convert to Boolean with the value true
    if (getNumber(arr[i]) || getNumber(arr[i]) === 0) {
      return getNumber(arr[i])
    }
  }
}

Copy the code

instanceofType of protection

The instanceof operator is also a native operator provided in JavaScript and is used to determine whether an instance was created by a constructor or a class using ES6 syntax. Type protection can also be implemented in TypeScript using the instanceof operator, as shown in the following example:

/** * Since instanceof only supports reference types and does not support primitive types, we need to change the array as follows: */
const arr2: (Number | String) [] = [new String('Prosperity on the other shore'), new Number(10)]
// 1. Define a function to get a number
const getNumber: (value) = > number = (value): number= > {
  // Check whether the current type is Number and return the current value as a string
  if (value instanceof Number) {
    return Number(value)
  }
}
// 2. Call to get the final value
const getValue: (arr: (Number | String) []) = > number = (
  arr: (Number | String) [],) :number= > {
  for (let i = 0; i < arr.length; i++) {
    // If a number is returned, convert to Boolean with the value true
    if (getNumber(arr[i]) || getNumber(arr[i]) === 0) {
      return getNumber(arr[i])
    }
  }
}
Copy the code

Two things to note when using Instanceof:

  • Applies only to any reference type; primitive types are not supported.
  • The former prototype is on the chaincontainsThe latter prototype object.

Second, the function

11. Function definition

The function declaration in TS is similar to that in JS, except that the return value type is fixed. If there is no return value, the return value type must be void instead of empty.

Let’s redeclare these three functions as TS:

Use the function keyword to declare a function:

/* Function Function name (parameter 1: type, parameter 2: type...) : Return value type {function body} */
function add4(x: number, y: number) :number {
    return x + y
}
Copy the code

The second way is to declare functions literally

/* const function name = function (parameter 1: type, parameter 2: type...) : Return value type {function body} */
const add5 = function (x: number, y: number) :number {
    return x + y
}
Copy the code

Third: declare functions using arrow functions

/* Const function name = (parameter 1: type, parameter 2: type...) Return value type => {function body} */
// 3. Declare functions using arrow functions
const add6 = (x: number.y: number) :number= > {
    return x + y
}
Copy the code

So that’s how you declare functions in TS. In JS, there is a case of parameter decoupling assignment. How to specify the parameter type in TS? Example code is as follows:

const add7 = ({ x, y }: { x: number; y: number}) :number= > {
    return x + y
}

Copy the code

There is also a more readable version of TS, as follows:

const add8: (baseValue: number, increment: number) = > number = function (
    x: number,
    y: number
) :number {
    return x + y
}

Copy the code

In this way, the function is divided into two parts: = is preceded by the return value type of the function type, and the latter part is where the function is defined.

In fact, the first part is to increase the readability of the code, not too much practical significance.

12. Optional and default parameters

Every function in TypeScript is required. This does not mean that null and undefined cannot be passed as arguments, but rather that a value is passed for each argument and an exception will be thrown if there is a mismatch. The number of parameters is the same as the number of arguments.

The sample code looks like this:

function add(x: number, y: number) :number {
    return x + y
}
let result1 = add(1) // Expected 2 arguments, but got 1.
let result2 = add(1.2)
let result3 = add(1.2.3) // Expected 2 arguments, but got 3

Copy the code

In JS each parameter is optional and can be passed or not, if not, it will be undefined by default.

We can do this in TS, we just add? Can realize the function of optional parameters. The following code

// Enable the optional parameter function
// Add one next to the parameter name. Can be
function add(x: number, y? :number) :number {
  return x + y
}
let result1 = add(1)
let result2 = add(1.2)
// let result3 = add(1, 2, 3) // Expected 2 arguments, but got 3

Copy the code

The code above implements optional arguments

Implementing the default parameter in TS is the same as implementing the default parameter in JS. You only need to assign a value to it. The sample code looks like this:

; (function () {
  function add(x: number, y: number = 2) :number {
    return x + y
  }
  let result1 = add(1) / / 3
  let result2 = add(1.2) / / 3}) ()Copy the code

Of course, if you don’t specify a type for y it’s just the same as in JS.

13. Remaining parameters

The remaining arguments are two arguments passed when the function is defined and three arguments passed when the function is called; Now you have one extra parameter, which is the remaining parameter.

In JS we can use arguments to access extra passed arguments. So how do I access the rest of the arguments in TS?

In fact, TS can store all of its arguments in a single variable, which is essentially a decoupled array. Example code is as follows:

function fun(x: number. numbers:number[]) :void {
    console.log(numbers)
}
fun(1.2.3.4) // [2, 3, 4]
Copy the code

Three, interfaces,

One of the core principles of TS is to type check the structure you have. Interfaces are used to name these types and define contracts for your code or third-party code.

Code that is ultimately compiled into JavaScript code without interfaces and type constraints.

14. Interface definition

The interface works similarly, but differently, to the type keyword. Type can define simple data types, such as the following code

type str = string
Copy the code

You can’t write that in an interface, you can only write function types and class types and array types.

Define the interface in TS using the interface keyword.

The sample code looks like this:

// Define a simple interface
interface Person {
  name: string
}
// Define the get method
function getPersonName(person: Person) :void {
  console.log(person.name)
}
// Define the set method
function setPersonName(person: Person, name: string) :void {
  person.name = name
}
// Define a person object
let person = {
  name: 'A bowl of porridge',
}
setPersonName(person, 'Bowl Week')
// The modification succeeded
getPersonName(person) / / a bowl of weeks


Copy the code

The Person interface is like a name that describes the requirements for using the interface, such as the need for a name attribute of type String.

It is important to note that type checking does not check the order of the attributes, only that the corresponding attributes exist and are of the same type.

15. Interface attributes

Optional attribute

If an attribute in an interface is optional, or exists only under certain conditions, you can add one next to the attribute name. Number. Example code is as follows:

; (function () {
  // Define a simple interface
  interface Person {
    name: string
    Age is optionalage? :number
  }
  // Define a person object
  let person = {
    name: 'Bowl Week'.age: 18.hobby: 'coding',}// Define the get method
  function getPersonName(person: Person) :void {
    // console.log(person.age, person.hobby) // Property 'hobby' does not exist on type 'Person'.
  }
})()


Copy the code

The sex property is optional, but the hobb property, since it is not defined in the interface, will throw an exception.

Read-only property

If you want a property to be a read-only property, you simply add readOnly to the property. Example code is as follows:

; (function () {
  interface Person {
    // Set name to read-only
    readonly name: string
  }
  // Define a person object
  let person = {
    name: 'Bowl Week',}// Define the set method
  function setPersonName(person: Person, name: string) :void {
    person.name = name // Cannot assign to 'name' because it is a read-only property.
  }
  setPersonName(person, 'A bowl of porridge')
})()

Copy the code

16. Interface inheritance

Like classes, interfaces can inherit from each other. This allows us to copy members from one interface to another, giving us more flexibility to split the interface into reusable modules.

Example code for interface inheritance using the extends keyword is as follows:

// Define two interfaces
interface PersonName {
  name: string
}
interface PersonAge {
  age: number
}

// Define a Person interface to inherit from the above two interfaces
interface Person extends PersonName, PersonAge {
  hobby: string
  // Define a method that returns string
  say(): string
}
let person = {
  name: 'Bowl Week'.age: 18.hobby: 'coding'.// Sample methods
  say() {
    return 'Bowl Week'}},// Define the get method
function getPersonName(person: Person) :void {
  console.log(person.name, person.age, person.hobby)
}
getPersonName(person) // One bowl week 18 coding


Copy the code

Inherit multiple interfaces, separated by commas.

17. Describe the function type interface

Interfaces in TS can also describe interfaces of function type.

The definition of a function type interface is like a function definition with only a parameter list and a return value type, in which each parameter needs a name and type. The sample code looks like this:

interface MyAdd {
    (x: number.y: number) :number
}
Copy the code

Once the definition is complete, we can use this function interface just like we would use a normal interface. The sample code looks like this:

let myAdd: MyAdd = (x: number.y: number) :number= > {
    return x + y
}
Copy the code

The code above is equivalent to the code defined by the following function:

let myAdd: (x: number, y: number) = > number = (
    x: number.y: number) :number= > {
    return x + y
}
Copy the code

Four, class,

18. Define the class

The class keyword is also used to define a class in TS. Example code is as follows:

; (function () {
  / / define the class
  class Person {
    // Public attribute, can not write by default
    public name: string
    // constructor
    constructor(name: string) {
      // Initialize the name attribute
      this.name = name
    }
  }
  // instantiate the class
  const person = new Person('Bowl Week')
  console.log(person.name) / / a bowl of weeks}) ()Copy the code

The class defined above has a constructor and a public attribute name, and the constructor constructor is called when the class is instantiated, which initializes the function properties.

There is also a shorter version of the above, as follows:

; (function () {
  class Person {
    constructor(public name: string){}}// instantiate the class
  const person = new Person('Bowl Week')
  console.log(person.name) / / a bowl of weeks}) ()Copy the code

So this is the same thing as this.

19. The inheritance

An important feature of object-oriented programming languages is inheritance. Inheritance is extending an existing class based on a class.

For example, father has a siheyuan in Beijing, the son can inherit the father’s siheyuan, but also can buy a villa; The son’s property eventually included a courtyard house and a villa in Beijing.

Example code for inheriting the extends keyword in TS is as follows:

; (function () {
  // Define a base class, also known as a superclass
  class Person {
    // Define a name attribute in the base class
    constructor(public name: string){}}// Define a derived class, also called a subclass, that inherits from the base class
  class Programmer extends Person {
    constructor(name: string.public hobby: string) {
      // Call the base class constructor through super
      super(name)
    }
  }
  // instantiate the subclass
  const programmer = new Programmer('Bowl Week'.'coding')
  console.log(programmer.name, programmer.hobby) // A bowl of weekly coding}) ()Copy the code

In the example code above, Person is called the base class, or superclass, and Programmer is a derived class, or subclass.

In the example above, the Programmer class inherits from the Person class through the extends keyword. A subclass has all the attributes and methods of its parent class.

In the constructor of the subclass, we must call the super() method to execute the constructor of the base class, which is required.

Class inheritance can not only inherit the class, but also override the parent class properties or methods in the subclass. Example code is as follows:

// Define a Person class
class Person {
    constructor(public name: string) {}
    // Define a method
    sayMy() {
        console.log('My name:The ${this.name}`)}}// Define an Adult class that extends from the Person class
class Adult extends Person {
    constructor(public age: number) {
        super('Prosperity on the other shore')}// Override the parent method
    sayMy() {
        console.log('My name:The ${this.name}Age:The ${this.age}`)}}// Define a Programmer class that extends from the Adult class
class Programmer extends Adult {
    constructor(public hobby: string) {
        super(18)}// Override the parent method
    sayMy() {
        console.log(
            'My name:The ${this.name}Age:The ${this.age}Hobbies:The ${this.hobby}`)}}// Class instantiation
const programmer = new Programmer('coding')
programmer.sayMy() // My name: Biobaofang Age: 18 Hobby: coding

Copy the code

First, we define a Person class, in which we define an attribute and a method. Then we define an Adult class that inherits from the Person class and overwrites the Person methods. This class inherits from the Adult class and overwrites the methods of the Adult class. That is, the sayMe() method has all the attributes and methods of the Person and Adult classes. This means that the Programmer class has three properties and one method.

20. public,privateandprotectedThe modifier

The difference between public, private and protected modifiers:

  • public: public, we can freely access the members defined in the class. TS by defaultpublic
  • private: private, only defined members can be accessed inside the class, not outside the class
  • protected: protected and can access defined members in this class or a subclass.

The sample code looks like this:

// Define a Person class with public and private members and protected members.
class Person {
  public name: string
  Private members usually start with an _
  private _age: number
  protected hobby: string
  // Attribute initialization
  constructor(name: string, age: number, hobby: string) {
    this.name = name
    this._age = age
    this.hobby = hobby
  }
  sayMy() {
    console.log(this.name, this._age, this.hobby)
  }
}

// Instantiate the Person class
const person = new Person('Bowl Week'.18.'coding')

console.log(person.name) / / a bowl of weeks
// Out-of-class access to a private member throws an exception
// console.log(person._age) // Error reported
// An out-of-class access protection member throws an exception
// console.log(person.hobby) // Error reported

// Private and protected members can be accessed within the class
person.sayMy() // One bowl week 18 coding

// Define a class to inherit from the Person class
class Programmer extends Person {
  constructor(name: string, age: number, hobby: string) {
    super(name, age, hobby)
  }
  sayMy() {
    console.log(this.name) / / a bowl of weeks
    // Private members of the parent class cannot be accessed in subclasses
    // console.log(this._age) // Error
    // Access protected members in subclasses
    console.log(this.hobby) // coding}}// Instantiate the Programmer class
const programmer = new Programmer('Bowl Week'.18.'coding')
programmer.sayMy()

// Make sure to conflict with members in other code
export {}

Copy the code

In the code above, we can access public members, private members, and protected members in the base class, but we can only access public members outside the class. When we define a subclass that inherits from the Person class, we can access protected members in the subclass, but not private members.

21. The getter and setter

We are not really unable to read and write. Getters and setters are provided in TS to help us effectively control access to object members.

The sample code looks like this:

// Define a Person class
class Person {
  Private members usually start with an _
  private _name: string
  // Attribute initialization
  constructor(name: string) {
    this._name = name
  }
  // Get the private _name attribute value
  get getName() :string {
    return this._name
  }
  // Set the private _name property value
  set setName(name: string) {
    this._name = name
  }
}
// instantiate the class
const person = new Person('A bowl of porridge')
// Get it by getName
console.log(person.getName) / / a bowl of porridge
// Set the value of _name by setName
person.setName = 'Bowl Week'
// Retrieve
console.log(person.getName) / / a bowl of weeks

Copy the code

22. readonlyThe modifier

We can make a property read-only with the readonly modifier. Read-only attributes must be initialized either at declaration time or in a constructor. The sample code looks like this:

// Define a class with a read-only attribute
class Person {
  // readonly name: string
  / / is equivalent to
  // public readonly name: string
  // constructor(name: string) {
  // this.name = name
  // }
  / / or
  constructor(public readonly name: string){}}/ / instantiate
const person = new Person('Bowl Week')
console.log(person.name) / / a bowl of weeks
// Change the value of name
// person. Name = 'week' // error! Name is read-only.

Copy the code

23. Static members

In TS we can also create static members, properties or methods that exist in the class itself rather than on an instance of the class. Static member definitions in TS use the static keyword as in ES6.

The sample code looks like this:

class Hero {
  / / counter
  static count = 0
  constructor(public name: string) {
    // For each attribute created count ++
    ++Hero.count
  }
}
// Instantiate a Hero class
const hero1 = new Hero(Sun Wukong)
console.log(Hero.count) / / 1
const hero2 = new Hero('which zha')
console.log(Hero.count) / / 2

Copy the code

Here we use static properties to implement a counter that instantiates several classes with one record.

24. An abstract class

To understand what is abstract class, we need to understand what is abstract first. The so-called abstraction is to extract common and essential features from many things and abandon their non-essential features. Apples, bananas, pears, grapes, peaches, etc., all have the same characteristic as fruit. The process of deriving the concept of fruit is an abstract process.

Abstract class is to separate the common functions of many classes and create a single class to be used as the base class of other derived classes. They are not allowed to be instantiated. Define abstract classes using the abstract keyword.

An abstract method is one that has a method definition but no method body, which needs to be implemented in a subclass.

Example code is as follows:

// Define an abstract class with the abstract keyword. This class does not need to be initialized and is used only as a base class
abstract class Department {
  // Initializes the name member, parameter attribute
  constructor(public name: string) {}

  printName(): void {
    console.log('Department Name:' + this.name)
  }
  // The abstract method must contain the abstract keyword
  abstract printMeeting(): void // Must be implemented in a derived class
}

class AccountingDepartment extends Department {
  constructor() {
    super('Accounting Department') // Super () must be called in the constructor of a derived class
  }

  printMeeting(): void {
    console.log('The accounts department is in charge of the money')}}// const department = new department () // Throws an exception: an instance of an abstract class cannot be created
// Instantiate the abstract subclass
const department = new AccountingDepartment()
// Call a method in an abstract class
department.printName() // Department: Accounting Department
// Invoke an abstract method implemented in a derived class
department.printMeeting() // The Accounting Department is the money department


Copy the code

25. Classes and interfaces

A class definition creates two things: an instance type of the class and a constructor. Because classes can create types, just like interfaces we learned earlier, we can use classes where interfaces are used. The sample code looks like this:

// Define a class
class Point {
    x: number
    y: number
}
// Define an interface inheritance and class
interface Point3d extends Point {
    z: number
}

let point3d: Point3d = { x: 1.y: 2.z: 3 }

Copy the code

The class implements an interface through Implement, as shown in this example:

// Define the interface
interface Eat {
  eat(food: string) :void
}
interface Run {
  run(distance: number) :void
}

// Define the class implementation interface
class Person implements Eat.Run {
  eat(food: string) :void {
    console.log(food)
  }
  run(distance: number) {
    console.log(distance)
  }
}
export {}

Copy the code

Fifth, generics

26. The usage

Now we define a join function that accepts two values of the same type and returns the concatenated value of the two parameters. Example code is as follows:

// By generics, we mean a generic type
// Define a join function that takes two arguments of the same type and returns the concatenation of the two parameters.
function join<T> (first: T, second: T) {
  return `${first}${second}`
}
// T is a string
join<string> ('the first'.'2') // First and second
// In this case, the compiler automatically inferred the type from the parameters passed in
join(1.2) / / 12

Copy the code

Generics are defined by <> Angle brackets. When we define the join function, we do not know which types we can accept, but we do know that the two types must be the same. If we want to satisfy this requirement, it is not so easy without generics.

There are two ways to call a function: one is to specify the type as string directly; The other is through type inference, where the editor automatically helps us determine the type based on the parameters passed in.

27. Use generics in functions

When defining a function, we can use multiple generics, and the return value type can be specified by generics, as long as the number and usage match. Example code is as follows:

function identity<T.Y.P> (first: T, second: Y, third: P) :Y {
  return second
}
// Specify the type
identity<boolean.string.number> (true.'String'.123) / / string
// Type inference
identity('string'.123.true) // true


Copy the code

28. Use generics in classes

We can use generics not only in functions, but also in classes. Example code is as follows:

class DataManager<T> {
  // Define a class that has a private array of type T
  constructor(private data: T[]) {}
  // Say the values in the array according to the index
  getItem(index: number): T {
    return this.data[index]
  }
}
const data = new DataManager(['Bowl Week'])
data.getItem(0) / / a bowl of weeks

Copy the code

Generics can also inherit from an interface, as shown in the following example:

interface Item {
  name: string
}
class DataManager<T extends Item> {
  // Define a class that has a private array of type T
  constructor(private data: T[]) {}
  // Say the values in the array according to the index
  getItem(index: number) :string {
    return this.data[index].name
  }
}
const data = new DataManager([{ name: 'Bowl Week' }])
data.getItem(0) / / a bowl of weeks

Copy the code

Using extends serves the purpose of a generic constraint. In the case of the above code, we must constrain that the value passed must have a name attribute, or else an exception will be thrown.

29. Use type parameters in generic constraints

Suppose we define a class with a private object that contains attributes. Then define a method that retrieves its corresponding value via key. The implementation code is as follows:

// Define an interface
interface Person {
  name: string
  age: number
  hobby: string
}
// Define a class
class Me {
  constructor(private info: Person) {}
  getInfo(key: string) {
    return this.info[key]
  }
}
const me = new Me({
  name: 'Bowl Week'.age: 18.hobby: 'coding',})// Calling me.getInfo() may get undefined as shown in the following example
me.getInfo('myName') // undefined

Copy the code

In the above code, if we pass in a missing property when we call the getInfo() method on the instantiation object, we get undefined. Calling a method that returns undefined is not the TypeScript style.

This problem can be solved by using the keyof operator, which can be used to get all keys of a type whose return type is the union type. Example code is as follows:

type myPerson = keyof Person // 'name' | 'age' | 'hobby'

Copy the code

Now you can use this operator to solve the problem above. Example code is as follows:

class Me {
  constructor(private info: Person) {}
  // This is the same as the following
  getInfo<T extends keyof Person>(key: T): Person[T] {
    return this.info[key]
  }
  // getInfo<T extends 'name' | 'age' | 'hobby'>(key: T): Person[T] {
  // return this.info[key]
  // }
}
const me = new Me({
  name: 'Bowl Week'.age: 18.hobby: 'coding',})// Calling me.getInfo() will compile an error if an unknown attribute is passed
me.getInfo('myName') // error: Parameters of type "myName" cannot be assigned to parameters of type "keyof Person".

Copy the code

Now we just need to access properties that don’t exist in the object and the compiler will get an exception.

Namespaces

30. Definition and use of namespaces

The definition of a namespace is equivalent to defining an object in which variables, interfaces, classes, methods, and so on can be defined, but which cannot be accessed externally unless the export keyword is used to specify that this content is externally visible.

Next define a regular validation of a. Ts file, the implementation code is as follows:

// validation.ts
// Create a namespace named Validation with namespace
namespace Validation {
    // Define a regular expression
    const isLetterReg = /^[A-Za-z]+$/
    // The regular expression is exported by export
    export const isNumberReg = / ^ [0-9] + $/
    // Export a method
    export const checkLetter = (text: any) = > {
        return isLetterReg.test(text)
    }
}

Copy the code

In the above code, we define a namespace named Validation, define two attributes and a method in it, and export one attribute and one method (the export in the namespace uses the export keyword).

To use the contents of a namespace in a file, just use ///
where the external namespace is used, note the triple slash ///, and specify the path of the namespace file relative to the current file in the path property. The specific code is as follows:

// index.ts
/// 
      
let isLetter = Validation.checkLetter('text')
const reg = Validation.isNumberReg
console.log(isLetter)
console.log(reg)

Copy the code

Note the ///
on the first line. The syntax structure can’t be wrong, otherwise the compilation will fail.

The compile command is as follows:

tsc --outFile src/index.js index.ts
Copy the code

The outFile parameter is used to merge the output files into a single file

The compiled index.js file looks like this:

// Create a namespace named Validation with namespace
var Validation;
(function (Validation) {
    // Define a regular expression
    var isLetterReg = /^[A-Za-z]+$/;
    // The regular expression is exported by export
    Validation.isNumberReg = / ^ [0-9] + $/;
    // Export a method
    Validation.checkLetter = function (text) {
        return isLetterReg.test(text);
    };
})(Validation || (Validation = {}));
/// 
      
var isLetter = Validation.checkLetter('text');
var reg = Validation.isNumberReg;
console.log(isLetter);
console.log(reg);

Copy the code

31. Split into multiple files

As we continue to develop, we can split the same namespace into multiple files for maintenance. Although we split the namespace into multiple files, they still belong to the same namespace, as shown in the following code:

LetterValidation.ts

// LetterValidation.ts
namespace Validation {
    export const isLetterReg = /^[A-Za-z]+$/
    export const checkLetter = (text: any) = > {
        return isLetterReg.test(text)
    }
}

Copy the code

NumberValidation.ts

// NumberValidation.ts
namespace Validation {
    export const isNumberReg = / ^ [0-9] + $/
    export const checkNumber = (text: any) = > {
        return isNumberReg.test(text)
    }
}

Copy the code

index.ts

// index.ts
/// <reference path="./LetterValidation.ts"/>
/// <reference path="./NumberValidation.ts"/>
let isLetter = Validation.checkLetter('text')
const reg = Validation.isNumberReg
console.log(isLetter)

Copy the code

Let’s use the command line to compile:

tsc --outFile src/index.js index.ts
Copy the code

The final compiled index.js code is as follows:

// LetterValidation.ts
var Validation;
(function (Validation) {
    Validation.isLetterReg = /^[A-Za-z]+$/;
    Validation.checkLetter = function (text) {
        return Validation.isLetterReg.test(text);
    };
})(Validation || (Validation = {}));
// NumberValidation.ts
var Validation;
(function (Validation) {
    Validation.isNumberReg = / ^ [0-9] + $/;
    Validation.checkNumber = function (text) {
        return Validation.isNumberReg.test(text);
    };
})(Validation || (Validation = {}));
/// <reference path="./LetterValidation.ts"/>
/// <reference path="./NumberValidation.ts"/>
var isLetter = Validation.checkLetter('text');
var reg = Validation.isNumberReg;
console.log(isLetter);

Copy the code

As you can see from the compile result, we import letterValidation. ts first, then numberValidation. ts, and their final compiled results are compiled in the order they were imported.

32. Namespace alias

An alias is a simplified namespace operation that uses the import keyword syntax as follows:

import q = x.y.z
Copy the code

Note that this approach is not to be confused with the parent module’s import x = require(‘name’) syntax, which creates an alias for the specified symbol. You can use this method to create aliases for arbitrary identifiers, including imported objects in a module.

// Define a namespace
namespace Shapes {
    // Define a subnamespace in the namespace and export it
    export namespace Polygons {
        export class Triangle {}
        export class Square {}}}// Rename the exported subnamespace to Polygons using the syntax of import
import polygons = Shapes.Polygons
// Instantiate the Square class with the exported namespace
let sq = new polygons.Square()

Copy the code

As you can see from this example, using the import keyword to define an alias for an output element in the namespace can reduce the cost of obtaining attributes in depth.

Seven, modules,

The TypeScript module system is ES6 compliant, and learning about TypeScript modules is especially easy if you are familiar with ES6 modules.

33. exportandimport

Export is still used in TypeScript to export declarations that include not only variables, functions, and classes, but also typescript-specific aliases and interfaces.

For aliases, click on Aliases. Interface Click interface to learn

Sample code for the export looks like this:

// export.ts
// Export interface
export interface Person {
    name: String
}
/ / derived classes
export class MyClass {
    constructor(public person: Person){}}let myClass = new MyClass({ name: 'Bowl Week' })
// Alias the exported object through as
export { myClass as person }

Copy the code

In the above code, we can not only export interfaces, classes, and so on; In addition, you can use the AS keyword to change the exported name.

The code for importing looks like this:

// import.ts
import { Person } from './export' // Import the interface
// const myName: Person = 'one week' // error cannot assign type 'string' to type 'Person'.
/ / import
import { MyClass } from './export'
// Instantiate the object from the imported class
const person = new MyClass({ name: 'Bowl Week' })
console.log(person) // MyClass {person: {name: 'person'}}

// Change the name of the imported object to as
import { person as myClass } from './export'
console.log(myClass) // MyClass {person: {name: 'person'}}

Copy the code

Here we refer to module files without the.ts suffix, which refers to a module resolution strategy.

Take export(the file name, not the keyword) as an example. When parsing a module reference, the compiler will look for.ts,.tsx, and.d.ts files with the suffix omitted. If not, the module path specified by the types field is searched in package.json in the current folder, and then the module is searched through this path. If package.json file or types field is not found, export will be searched as a folder. If it is indeed a folder, index. TSX and index.d.ts will be searched under this folder in sequence. If not, the search will continue in the parent folder of the export folder in the above example. The search rules are in the same order as the previous ones.

We can export the declaration we need through the export default keyword. When importing the import statement, we do not need curly braces. We can write a default name for it.

The sample code looks like this:

// export-default
interface Person {
    name: String
}
class MyClass {
    constructor(public person: Person){}}let myClass = new MyClass({ name: 'Bowl Week' })
export default { MyClass, myClass }

Copy the code
// import.ts
import test from './export-default'
console.log(test.MyClass) // [Function: MyClass]
console.log(test.myClass) // MyClass {person: {name: 'person'}}

Copy the code

34. export =import = require()

TypeScript compiles code to CommonJS, AMD, or other modular system code and generates a corresponding declaration file. We know that CommonJS and AMD module syntaxes are incompatible, so TypeScript adds export = and import xx = require() statements to support both modules.

When we want to export a module, we can use export = to export:

// export=.ts
interface Person {
    name: String
}
class MyClass {
    constructor(public person: Person){}}let myClass = new MyClass({ name: 'Bowl Week' })
export = { MyClass, myClass }

Copy the code

Modules then exported using this form must be imported using import xx = require() :

// import = require()
import test = require('./export=')
console.log(test.MyClass) // [Function: MyClass]
console.log(test.myClass) // MyClass {person: {name: 'person'}}

Copy the code

If your module does not need to support both module systems, you can export content without using export =.

Write in the last

That’s the TypeScript summary of spitting blood, and this article can be used as a reference. If our article is helpful to you, please give me a small thumbs-up, thank you ~.

Phase to recommend

  • [Suggested collection] Summarized 42 front-end layout schemes
  • How Does the Wheel Run? From zero to one, how do you develop a scaffold
  • Front-end modular details (CommonJS, AMD, CMD, ES Module)