Ten years without sleep, painstakingly wrote the experience

summary

  • TS is a programming language based on JS
  • The problem of JS type system is solved
  • TS greatly improves the reliability of code

Key issues

  • Problems of JS own type system
    • How to solve this problem with some excellent technical solutions
    • TS is just a language we will involve in this process
    • Because TS is the ultimate solution to such problems, we will focus on it
    • In addition, some other related technical solutions will also be introduced

Content summary

  • Strong type and weak type
  • Static type and dynamic type
  • Problems of JS own type system
  • TS language specification and basic usage

Strong type and weak type

  • Nouns often mentioned when distinguishing between different programming languages
    • Strong and weak types (type safe)
    • Static and dynamic types (type checking)
  • Type safety
    • Strong type vs weak type
      • Strong type: at the language level, the argument type of the restricted function must be exactly the same as the formal parameter type
      class Main {
          static void foo (int num) {
              System.outprintln(num);
          }
          public static void main(String[]) {
              Main.foo(100);  // 100
              Main.foo("100"); // error "100" is a string
              Main.foo(Integer.parseInt("100")); // ok
          }
      }
      
      • Weak type: the type of argument is not restricted at the language level
      function foo (num) {
          console.log(num)
      }
      foo(100)    // ok
      foo('100')  // ok
      foo(pareseInt('100'))   // ok
      
    • Because this type of strong and weak is not the definition of an authority at all, and there is no specific rule, there is a different understanding of the details of this definition
    • But on the whole, the way we define is to describe:
      • Strong types have stronger type constraints
      • There are few constraints in weak types
    • I agree with the following statement:
      • Arbitrary implicit type conversion is not allowed in strongly typed languages
      • Arbitrary implicit type conversion is allowed in weakly typed languages
    console.log('100' - 50) // 50
    
    console.log(Math.floor('foo'))  // NaN
    
    console.log(Math.floor(true))   // 1
    
    • Variable types are allowed to change at any time, not the difference between strong and weak types
      • For example, python is a strongly typed language, but its variable type can be changed at any time
      • This may be inappropriate in many materials, because most of them say that pythpn is a weakly typed language, but it is not

Static type and dynamic type

  • Type check
    • Static type and dynamic type
      • Static type: when a variable is declared, its type is explicit. After declaration, the type cannot be modified
      • Dynamic type: a type can only be defined in the running stage, and the type of variable can be changed at any time
        • In dynamic language, variables have no type, and the values stored in variables have types
        var foo = 100   // number type
        foo = 'bar' // character string
        console.log(foo)    
        

JS type system features

  • JS is a weakly typed and dynamically typed language
    • The type system of the language itself is very weak. Even we can say that JS has no type system
  • The characteristic of this language can be called: willfulness!
    • Because it has almost no type restrictions, JS is a particularly flexible language
    • But behind this appearance, what is lost is the reliability of the type system. Every variable we encounter in the code, we need to worry about whether it is the type I want
  • The overall feeling can be called: unreliable!
  • Why is JS not a strongly typed or statically typed help
    • Related to the background of JS, the application of JS did not expect to develop to today's scale. It is relatively simple, and the function may be completed by dozens of lines of code or more than 100 lines of code
      • In this case, the restriction of type system is superfluous or troublesome
    • JS is a script language, which is characterized by running directly in the running environment without compiling
      • In other words: JS does not have a compilation link, so even if it is designed as a static type language, it is meaningless, because static types need to be compiled and checked at the compilation stage, and JS does not have such a link at all
    • Therefore, JS is a more flexible and more variable weakly typed dynamic language
      • At that time, there was no problem and even an advantage at that time, but now the scale of front-end applications has been completely different. There are large-scale applications everywhere, so JS code is more and more complex and the development cycle is longer and longer. In this case, the previous advantage has become a short board

Weakly typed problems

  • The problem exception cannot be found until run time
const obj = {} 

/**
 * obj There is no such method in, but it can be written like this at the syntax level, but an error will be reported at run time
 * In other words, in JS, a weakly typed language, we have to wait until the runtime. In this case, we found type exceptions in the code
 * */ 
obj.foo()   // TypeError: obj.foo is not a function
/**
 * Moreover, if we do not execute the method immediately, we will execute it at a specific time
 * 
 * The program can't find exceptions when it just runs, and it won't throw errors until the code is executed
 * 
 * In other words, if we don't test this line of code in the process of testing, such a hidden danger will be left in the code
 * 
 * If it is a strongly typed language, we directly call a non-existent member of the object, which will report an error grammatically. We don't have to wait for the recovery to report an error
 * */ 
setTimeout(() => {
    obj.foo()   //TypeError: obj.foo is not a function
}, 1000)
  • If the type is not clear, the function function may change
function sum (a, b) {
    return a + b
}

console.log(sum(100, 200))  // 300
console.log(sum(100, '200'))    // 100200
/**
 * This is a typical problem caused by type uncertainty
 * 
 * Although we can avoid such problems through agreement, there is no guarantee for agreement, especially when multiple people cooperate in development
 * 
 * We can't guarantee that everyone will follow all the agreements
 * 
 * If we use a strongly typed language, this situation will be completely avoided, because in a strongly typed language, if we require a number to be passed in, the incoming string will directly report an error
 * */ 
  • An incorrect use of an object indexer
const obj = {}

obj[true] = 100

console.log(obj['true'])

/**
 * Let's create an index through the indexer
 * 
 * The property name of an object can only be a string or a Symbol introduced by ES6
 * 
 * However, because JS is weakly typed, you can use any type of value as the attribute name of the object in the indexer, which will be automatically converted into a string
 * 
 * Here we use Boolean value as the attribute name, which will eventually be converted to the form of string true
 * 
 * It may feel strange if we don't know the feature that object attribute names are automatically converted to strings
 * 
 * The root of this strange may be that we use a relatively casual weakly typed language. If it is a strongly typed language, this problem may be avoided
 * 
 * Because in strongly typed languages, he clearly has type requirements, and what he doesn't meet won't work grammatically
 * */ 
  • This is just the tip of the iceberg of weak type problems
  • The disadvantages of weakly typed language are very obvious. It can be avoided by agreement only when the amount of code is small. For some large projects with long development cycle, this kind of gentleman agreement still has certain hidden dangers. Only the mandatory requirements at the syntax level can provide a more reliable guarantee
  • Strong typed language code has obvious advantages in code reliability. Using strong typed language can eliminate a large number of possible type exceptions in advance without waiting for slow debug ging in the running process

Advantages of strong type

  • Errors exposed earlier
    • Eliminate most types of exceptions in advance in the coding phase
  • The code is smarter and more accurate
    • What developers need most
    function render (element) {
        // element.
        /**
         * At this time, the intelligent prompt cannot prompt the very useful content
        * 
        * This is because there is no way for development tools to infer the type of this element
        * 
        * So there is no way to know which specific members are here
        * 
        * At this time, you can only access the members of the object by memory
        * 
        * Therefore, it also leads to some problems such as spelling mistakes or name mistakes
        * 
        * If the compiler can provide a more accurate type, it can always know which language is more intelligent
        * */ 
    }
    
  • Refactoring is more reliable
    • When there are some destructive changes to the code, such as deleting a member in the object, or modifying an existing member name
    const util = {
        aaa: () => {
            console.log('util func')
        }
    }
    /**
    * If the aaa method of this object is used in many places, we can't modify it at will
    * 
    * Because JS is a weakly typed language, after modifying the name of this attribute, the name used in many places is still the same as before. Even if there is an error, it will not be displayed immediately
    * 
    * For strongly typed languages, once the attribute name changes, an error will be reported immediately. At this time, you can easily locate all the places used and then modify them
    * 
    * Even some tools can automatically modify all references to members of this object
    * */ 
    
  • Reduce unnecessary type judgment
    • Strongly typed languages reduce some of our type judgments at the code level
    function sum (a, b) {
        if (typeof a === 'number' || typeof b === 'number') {
            throw new TypeError('arguments must be a number')
        }
    
        return a + b
    }
    

TS language specification and basic usage

TS overview

  • A superset (extension and) language based on JS
    • Superset: some extension features are added on the original basis of JS
    • What's more, a more powerful type system and support for the new features of ES
    • It will eventually be compiled into the original JS
  • Advantages of type system in TS
    • It has been learned in Flow that the two are very similar, which is to help us avoid possible type exceptions in the development process and improve the efficiency and reliability of our code
  • Support for new features
    • ES has iterated many useful new functions in recent years, but there will be compatibility problems in old environments. TS supports automatic conversion of these new features, so new features can be used immediately in TS
    • Even if we don't need this strongly typed system, it's a good choice to use the new features of ES through TS
      • We used to use babel to convert some new features in JS. In fact, TS is similar to babel in this regard, because ts can finally choose the code with the lowest ES3 version, so the compatibility is particularly good
      • Because TS is finally compiled into JS to work, ts can be used for development in any JS running environment, such as traditional browser applications, node applications and react Native application
  • Compared with the previous Flow, TS, as a complete programming language, has more powerful functions and more perfect ecology, especially for development tools!
  • At present, many large projects begin to use TS development
    • Angular
    • Vue.js 3.0
  • TS - second language in front-end domain
    • Small projects - you need to be flexible and free, and you can choose JS itself
    • Large projects - TS recommended by all
  • shortcoming
    • Language itself has many more concepts
      • Interfaces, generics, enumerations
      • More of these concepts will increase our learning cost and time cycle. Fortunately, TS is progressive
        • Progressive: even if we don't know any features, we can immediately write TS code according to JS standard syntax. In other words, we can use it as JS, and then understand a feature in the learning process, so we can use a feature
    • For projects with relatively short cycle, TS will increase some costs
      • Because we need to write a lot of type declarations at the beginning of the project, such as objects and functions
    • If it is a large-scale project with long-term maintenance, the cost is nothing, and it is often once and for all, so the advantages of TS far outweigh the disadvantages
  • On the whole, TS will become a necessary language with the development of our front-end

TS quick use

  • install
    • Instruction:
      1. yarn init -yes || npm init -y
      2. yarn add typescript --dev || npm i typescript -d
      3. yarn tsc file name 𞓜 tsc file name
    • Because TS itself is an npm module, you can choose to install it to the global. However, considering the dependence of the project, it is more reasonable for us to install it into the project
  • After installation, this module will be installed on our node_ In modules There is an extra command of tsc in the bin directory
    • effect:
      • Help us compile TS code
        • First check the type usage exception in the code
        • Remove extended syntax such as type annotations
        • New features of automatic conversion
      • A more powerful type system

TS profile

  • tsc command can not only compile a specified TS file, but also be used to compile the whole project or the whole project
    • Create a configuration file before compiling
    • yarn tsc --init || tsc --init
  • In this file, there is only one attribute of compilerOptions by default
    • This attribute is the corresponding configuration option of TS compiler
    • Most of them are commented out, and there will be some brief descriptions on each option
  • Most commonly used options
    • target - sets the ES standard adopted by the compiled JS
    • module - how will the output code be modularized
    • outdir - folder to which the compilation results are output
    • rootDir - folder where the source code (TS) resides
    • sourceMap - enable source code mapping, which enables us to use sourceMap to debug TS source code during debugging
    • Strict - turn on all strict check options, and the type check will become very strict
  • be careful:
    • We have tsconfig After the JSON configuration file, we can use this configuration file when we use the tsc command
    • However, if you still use tsc to compile a specified file, the configuration file will not take effect. The configuration file will take effect automatically only when you directly run tsc to compile the whole project
      • Direct tsc will generate not only a js file, but also the corresponding sourceMap file

Raw data type

  • Basic application of JS six original data types in TS
// Raw data type
const a: string = 'foobar'

const b: number = 100 // NaN Infinity

const c: boolean = true // false

/**
 * Compared with Flow, these three types are different. They are allowed to be empty in TS
 * 
 * However, it is shown as wrong here because of the difference between the strict mode and the default mode
 * 
 * You need to close strict, and then no error will be reported here
 * 
 * In non strict mode, string number boolean can be empty, and strict mode is not allowed
 * 
 * strict All strict mode options are enabled. If we just want to check, our variables cannot be empty
 * 
 * We can use strictNullChecks -- check that the variable cannot be empty
 * */ 

// const d: string = null

/**
 * void --- Null value is generally used to mark the type of function return value when the function has no return value
 * 
 * Can only store null/undefined. In strict mode, it can only be undefined
 * */ 
const e: void = undefined

const f: null = null

const g: undefined = undefined

/**
 * Only values of type Symbol() can be stored
 * 
 * Errors will be reported when using it directly. For details, listen to the next section~
 * */ 
// const h: symbol = Symbol()

Standard library declaration

  • Built in object type
  • Above, we try to use the global symbol function. Creating a symbol will report an error
  • Why?
    • Symbol is actually a built-in standard Object of JS, just like Array. The nature of Object is the same, but symbol is added in ES6. In fact, for this built-in Object, it also has types, and these built-in types have been defined for us in TS
  • solve:
    • Change the target to es2015
    • lib option to specify the standard library we refer to
      • When you open the console inside the 01 file, an error will be reported
        • The console object is provided by the BOM object in the browser, and only ES2015 is set in the lib we just set, so all the standard libraries have been overwritten. Here, you need to add back all the default standard libraries
        • In TS, BOM/DOM are classified into a DOM standard library
  • Standard library
    • Declaration file corresponding to the built-in object
    • If you use built-in objects in your code, you must reference the corresponding standard library. Otherwise, TS will report an error if it cannot find the corresponding type

Chinese error message

  • TS supports multilingual error messages. By default, an error message language will be selected according to the language settings of the operating system and development tools
  • Add a -- locale zh CN when using the tsc command
yarn tsc --locale zh-CN || tsc --locale zh-CN

Scope problem

  • In the process of learning ts, we will certainly involve trying some different features of TS in different files. In this case, we may encounter the situation that there are the same variable names in different files
// const a = 123
/**
 * const a: 123
 * Cannot redeclare block range variable 'a'. ts(2451)
 * 02-Raw data type ts(2, 7): a is also declared here.
 * */ 
  • solve
    1. Self calling function
    (function () {
        const a = 123
    })()
    
    1. export is used, that is, ESmodules is used. In this way, the file can be regarded as a file module. The module has a separate module scope
    const a =123
    
    export {}
    
  • This kind of problem is generally not used in actual development, because in most cases, each file will work with a module, but in each case behind us, it is inevitable to use some repeated variables

Object type

  • Object in TS does not only refer to object types, but refers to all non original types
    • Object, array, function
  • To limit object types in TS, we should use more professional interfaces, which will be introduced in detail
  • Here we only need to know two points
    1. Object type refers not only to the object, but to other types besides the original type
    2. Due to the type limitation of the object, we can use this syntax similar to the literal amount of the object, but we can use the interface in a more professional way
export {} //It is only for the scope, which is not required for actual development

const foo: object = function () {} // {} []  function(){}

/**
 * If we need a common object type, we need to use syntax similar to object literal to mark it
 * 
 * The restriction object obj must have a foo attribute, and the attribute value must be number. When there are multiple members, continue to add them with comma intervals
 * 
 * Requirements of object type restriction: the object structure of assignment must be completely consistent with the type structure here, no more or less
 *
 * In other words, if there is one more attribute, the syntax will report a type error
 * 
 *  */ 
const obj: { foo: number, bar: string } = {foo: 123, bar:'TS'} 

Array type

  • The way of defining arrays in TS is exactly the same as that of Flow, and it also has two ways
    1. Using Array generics
    2. Use element type + []
    // An array of pure numbers
    const arr1: Array<number> = [1, 2, 3]
    const arr2: number[] = [1, 2, 3]
    
  • Advantages of using strong typing in TS
// function sum ( ...args ) {
//     return args.reduce((prev, current) => prev + current, 0)
// }
/**
 * If it is JS, we need to worry about the parameter types received by sum
 * 
 * Many times, we need to add a lot of judgment to ensure that each parameter is number
 * 
 * But in TS, you only need to add a numeric array type annotation to this type
 * */ 
function sum ( ...args: number[] ) {
    return args.reduce((prev, current) => prev + current, 0)
}

// sum(1, 2, '3') / / an error will be reported if an error value is passed in

Tuple type

  • Special data structure
    • Tuple - an array that specifies the number of elements and the type of each element
    • The element types of each element need not be exactly the same. In TS, the syntax similar to array literal can be used to define tuple types
  • It is generally used to return multiple return values in a function
    • In the hook of useState in React, we return a tuple type
    • Object in ES2017 The entries () method obtains all the key value arrays in an object. Each key value obtained is a tuple because it is a fixed length array
export {}

const tuple: [number, string] = [1, 'hhh']
// Only one number and string can be stored, neither more nor less

// Elements of tuples can be accessed using array subscripts
// const age = tuple[0]
// const name = tuple[1]

// The array structure can also extract each element
const [age, name] = tuple

Object.entries({
    foo: 123,
    bar: 456
})

Enumeration type

  • In the process of application development, we often use some values to represent a certain state
  • characteristic
    • You can give a group of values a better understood name
    • For an enumerated value, there are only a few fixed values, and there is no possibility of exceeding the range
  • Enumeration is a very common data structure in many traditional programming languages, but it does not exist in JS
  • Many times, we need to use an object simulation to implement enumeration
const PostStatus = {
    Draft: 0,
    Unpublished: 1,
    Published: 2
}

const post = {
    title : 'hello TS',
    content: 'TS is a typed superset of JavaScript',
    status: PostStatus.Draft // 2 // 1 // 0
}
  • Using a special enumeration type in TS, we can use the keyword enum to declare an enumeration
enum PostStatus {
    Draft = 0,
    Unpublished = 1,
    Published = 2
}

const post = {
    title : 'hello TS',
    content: 'TS is a typed superset of JavaScript',
    status: PostStatus.Draft // 2 // 1 // 0
}
  • Attention
    • The assignment number is used here instead of the colon
    • Enumerations can only be specified without using the assignment number. If they are not specified, they will accumulate from 0 by default,
    • If a specific value is specified for the first member of the enumeration, the value will be accumulated on this basis
    • The value of enumeration can be either number or string, that is, string enumeration
      • Since the string cannot be self incremented like a number, if it is a string enumeration, we need to manually initialize an explicit initialization value for each member
      • String enumeration is not common
    // enum PostStatus {
    //     Draft,
    //     Unpublished,
    //     Published
    // }
    
    // enum PostStatus {
    //     Draft = 6,
    //     Unpublished,
    //     Published
    // }
    
    enum PostStatus {
        Draft = 'a',
        Unpublished = 'b',
        Published = 'c'
    }
    
    enum PostStatus {
        Draft,
        Unpublished,
        Published
    }
    
    const post = {
        title : 'hello TS',
        content: 'TS is a typed superset of JavaScript',
        status: PostStatus.Draft // 2 // 1 // 0
    }
    
  • One more thing we need to know about enumeration
    • Enumeration types will invade our runtime code
    • In other words: it will affect our compiled results
    • Most of the types we use in TS will eventually be removed after compilation and conversion, but the enumeration will not, and will eventually be compiled into two-way key value pair objects
      • Two way key value pair - you can get the value through the key, or you can get the key again through the value
    • The purpose is
      • The name of the enumeration can be obtained dynamically according to the enumeration value, that is, the corresponding name can be obtained through the indexer in the code
    • If it is determined that the indexer will not be used in the code to access enumeration, we recommend that you use constant enumeration
      • The usage of constant enumeration is to add const before the keyword enum

Function type

  • Limits the input and output of functions, that is, parameters and return values
  • There are two ways to define functions in JS
    • Function declaration
    • Function expression
  • Here, you need to know how to constrain types of functions in two ways
export {}

// function fnuc (a: number, b: number): string {
//     return 'func'
// }
// The parameter is limited to number and the return value is limited to string; Formal parameters and arguments must be exactly the same, from type to quantity
// fnuc(1, 2)

// Optional parameters
// function fnuc (a: number, b?: number): string {
//     return 'func'
// }
// fnuc(1)

// Or use the default value of the parameter, or make it an optional parameter
// function fnuc (a: number, b: number = 100): string {
//     return 'func'
// }
// fnuc(1)

// Both optional and default parameters should appear at the end of the parameter list
// The parameters will be passed according to the position. If the optional parameters are in front of the required parameters, the required parameters cannot get the correct parameters

// Arbitrary parameter
function fnuc (a: number, b: number = 100, ...rest: number[]): string {
    return 'func'
}
fnuc(1)

// Function expression
const func1 = function (a: number, b: number): string {
    return 'func1'
}
/**
 * This function expression will eventually be put into a variable, and the variable receiving this function should also be typed
 * 
 * Generally, TS can infer the type of our variable from our function expression
 * 
 * If we pass a function as a parameter, that is, as a callback function
 * 
 * As a callback function, in this case, we must restrict the type of the parameter of the callback function
 * 
 * In this case, we can use the method similar to arrow function to indicate what kind of function our parameters can accept
 * 
 * This method is often used when defining interfaces
 * */ 

Any type

  • Because JS itself is a weak type, many built-in API s support receiving parameters of any type
  • TS is based on JS, so we will inevitably need to use a variable in the code to receive any type of data
  • be careful:
    • Any type is still a dynamic type, with the same characteristics as ordinary JS variables. It can accept any type of value, and it can also accept other types of values during operation
  • Because it may store values of any type, TS will not check the type of any, which means that we can still call any member on it like JS, and there will be no syntax error
  • However, it will still have the problem of type safety, so we should not use this type easily
function string (value: any) {
    return JSON.stringify(value)
}

string('string')
string(100)

Implicit type inference

  • In TS, if we do not mark a type through explicit type annotation, TS will infer the type of the variable according to the use of the variable - implicit type inference
export {}

let age = 18// TS will judge as number type
// age = 'tri' / / error reason: age has been judged as number

// This usage is equivalent to adding the type annotation of number to age

// When it cannot be inferred, it will be marked as any, such as:
let foo

foo = 18;
foo = 'string'
foo = true
  • Although TS supports implicit type judgment, and this implicit type inference can help us simplify part of the code, we still suggest that you add explicit types to each variable as much as possible, so that we can more intuitively understand our code later

Type Asserts

  • In some special cases, TS cannot infer the specific type of the variable, but as a developer, we can clearly know what type the variable is according to the use of the code
// Suppose it comes from an explicit interface
const nums = [110, 120, 119, 112]

const res = nums.find(i => i > 0)
/**
 * At this time, we clearly know that the return value must be a number
 * 
 * But TS infers that it is number or undefined
 * 
 * Then the return value cannot be used as a number
 * 
 * At this time, we can assert that the res is of type number
 * */ 
  • Assertion mode
    1. Use as keyword
    2. Use < >
    const num1 = res as number
    /**
    * At this point, the compiler can clearly know that our num1 is a number
    * */ 
    
    const num2 = <number>res
    /**
    * The two effects are the same, but she has a small problem with the way of angle brackets
    * 
    * When we use JSX in our code
    * 
    * The < number > here will have syntax conflicts with the tags in JSX
    * 
    * Recommended way to use as
    * */  
    
  • be careful:
    • Type assertion is not type conversion. It is not to convert one type to another, because type conversion is a concept of the code at runtime. In our place, type assertion is only a concept in the compilation process. After the code is compiled, the assertion will not exist

Interface

  • It can be understood as a specification or contract. It is an abstract concept that can agree on the structure of an object. When we use an interface, we must follow all the conventions of the interface
  • In TS, the most intuitive embodiment of the interface is that it can specify which members should be in an object and what types of these members are
export {}

interface Post {
    /**
     * You can use commas here, but the standard is to use semicolons, which can also be omitted
     * */ 
    title: string;
    content: string
}

function pointPost (post: Post) {
    console.log(post.title)
    console.log(post.content)
}
/**
 * This function has some requirements for the object. It must have the title attribute and content attribute
 * 
 * But this requirement is actually invisible and has no clear expression
 * 
 * In this case, you can use the interface to express the constraints
 * */ 

pointPost({
    'title': 'hi, TS',
    'content': 'hi'
})

  • One sentence summary: an interface is used to constrain the structure of an object. If an object implements an interface, it must have all the members of the interface

Interface supplement

  • There are also some special usages for members of the interface
    • optional member
      • If we are in an object and a member is dispensable, we can use the feature of optional member for the interface constraining the object
      • This usage is actually equivalent to marking its type with a specified type or undefined
      interface Post {
          title: string;
          content: string;
          subtitle?: string
      }
      
      const hello:Post = {
          'title': 'hi, TS',
          'content': 'hi'
      }
      
    • readonly member
      • After initialization, it cannot be modified again
      interface Post {
          title: string;
          content: string;
          subtitle?: string;
          readonly summary: string
      }
      
      const hello:Post = {
          'title': 'hi, TS',
          'content': 'hi',
          'summary':'It cannot be modified after initialization'
      }
      // hello. Summary = 'hhhh' / / error
      
    • Dynamic member
      • It is generally applicable to some objects with dynamic members, such as the cache object in the program. Some dynamic key values will appear during its operation
      interface Cache {
          [key: string]: string
          // The key is not fixed, the string is the type annotation of the key, and the outer string is the value set to this dynamic attribute
      }
      
      const cache: Cache = {}
      
      cache.foo = 'value'
      cache.foo = '123'
      

Basic use of class

  • Classes
  • Describe the abstract characteristics of a class of specific transactions
  • For example, mobile phone is a class, which is characterized by the ability to receive and make calls and send and receive text messages. Under this type, there will be some sub classes. This sub class will meet all the characteristics of the parent class, and then add some additional features, such as smart phone. In addition to receiving and making calls and sending and receiving text messages, it can also use some apps
  • We can't use this class directly, but use the specific affairs of this class, such as your smartphone
  • Class is the same. It can be used to describe the abstract members of a class of concrete objects. Before ES6, JS was implemented through function + prototype simulation. After ES6, JS has a special class
  • In TS, in addition to using the functions of all classes used in ES standards, some additional functions and usages are added, such as special access modifiers for class members and some concepts of abstract classes
class Person {
    /**
     * In TS, the attribute of a class must have an initial value, which can be assigned after =
     * 
     * It can also be initialized in the constructor
     * 
     * There must be one of them
     * */ 
    name: string // ='initial value'
    age: number // = 123
    constructor (name: string, age: number) {
        /**
         * In TS, we need to explicitly declare some attributes in the type
         * 
         * Instead of adding it dynamically through this directly in the constructor
         * */ 
        this.name = name
        this.age = age
    }
}
  • The attribute of a class must be declared in the type before it is used. In fact, the purpose is to mark some types of our attributes. In addition, we can declare some methods for this class according to the syntax in ES6

Access modifier for class

  • Private - marked as a private property, which can only be accessed inside the class
export {}

class Person {
    name: string
    private age: number // Marked as a private property, it can only be accessed inside the class

    constructor (name: string, age: number) {
        this.name = name
        this.age = age
    }
}

const tom = new Person('tom', 18)
console.log(tom.name)
// console.log(tom.age) / / an error is reported
  • Public - marked as a public member, which is the default in TS!
export {}

class Person {
    ...
    private age: number // Marked as a private property, it can only be accessed inside the class
    ...
}

const tom = new Person('tom', 18)
// console.log(tom.age) / / an error is reported
  • Protected - protected, external access is unavailable
export {}

class Person {
    ...
    protected gender: boolean
    ...
}

const tom = new Person('tom', 18)
console.log(tom.gender) // An error is reported and cannot be accessed externally
  • The difference between private and protected

    • protected only allows access to the corresponding members in this class
    export {}
    
    class Person {
        public name: string // Marked as public member, default in TS!
        private age: number // Marked as a private property, it can only be accessed inside the class
        protected gender: boolean
    
        constructor (name: string, age: number) {
            this.name = name
            this.age = age
            this.gender = true
        }
    }
    
    class Student extends Person {
        constructor(naem: string, age: number) {
            super(name, age)
            console.log(this.gender)    // Can access
        }
    }
    
  • effect:

    • Controls the accessibility level of members in some classes
  • Note:

    • The access modifier of the constructor is public by default. If it is set to private, this type cannot be instantiated or inherited externally. In this case, we can only add a static method inside this class, and then create an instance of this type in the static method, because private only allows internal access
    class Student extends Person {
        private constructor(naem: string, age: number) {
            super(name, age)
            console.log(this.gender)    // Can access
        }
    
        static create (name: string, age: number) {
            return new Student(name, age)
        }
    }
    // const jack = new Student()
    const jacked = Student.create('jack', 18)
    
    • If the type in the constructor is marked as protected, such a type cannot be instantiated externally, but compared with private, it is allowed to be inherited

Class

export {}

class Person {
    public name: string 
    private age: number 
    protected readonly gender: boolean

    constructor (name: string, age: number) {
        this.name = name
        this.age = age
        this.gender = true
    }

    sayHi (msg: string): void {
        console.log(`I am ${this.name},${msg}`)
    }
}


const tom = new Person('tom', 18)
console.log(tom.name)
tom.gender = false

Classes and interfaces

  • The concept of interface is more abstract than that of class
  • Let's take the previous examples of mobile phones for comparison. We say that mobile phones are a type. Examples of this type can receive and make calls and send text messages, because the characteristics of mobile phones are like this. However, we can make calls not only by mobile phones, but also by landline phones. However, landline phones do not belong to the category of mobile phones, but a separate category, because they can't send and receive text messages, In this case, there will be common features between different classes. For these common features, we generally use interfaces to abstract. Mobile phones can be understood as that mobile phones can also receive and make calls, because it implements this protocol, and landline phones can also make calls, because it also implements this protocol. The protocol here is called interface in the program
export {}

interface Eat {
    eat (food: string): void
}

interface Run {
    run (distance: number): void
}

class Person implements Eat, Run {
    eat (food: string): void {
        console.log(`Elegant dining: ${food}`)
    }
    run (distance: number): void {
        console.log(`Walking upright: ${distance}`)
    }
}

class Animal implements Eat, Run {
    eat (food: string): void {
        console.log(`Snoring food: ${food}`)
    }
    run (distance: number): void {
        console.log(`crawl: ${distance}`)
    }
}

abstract class

  • To some extent, it is similar to the interface. It can also be used to restrict that there must be a member in the subclass
  • An abstract class can contain some concrete implementations, while an interface can only be an abstraction of a member, not a concrete implementation
  • In general, it is recommended to use abstract classes for larger categories, such as the animal class just now. In fact, it should be abstract, because what we say about animals is only a general reference and not specific enough. There must be some more detailed classifications below it
abstract class Animal {
    eat (food: string): void {
        console.log(`Snoring food: ${food}`)
    }

    abstract run (distance: number): void
}

// After being defined as an abstract class, it can only be inherited and cannot be instantiated in the way of new

class Dog extends Animal {
    run (distance: number): void {
        console.log('Four corner crawling',distance)
    }
}

const d = new Dog()
d.eat('dog')
d.run(100)

generic paradigm

  • When defining functions, interfaces, or classes, we do not specify specific types. When we use them, we specify a feature of specific types
  • Taking a function as an example, generics means that when we declare this function, we do not specify a specific type, but pass a specific type when calling, in order to reuse our code to the greatest extent
export {}

// Creates an array of the specified length
// function createNumberArray (length: number, value: number): number[] {
//     const arr = Array<number>(length).fill(value)
//     return arr   
// }

// const res = createNumberArray(3, 100)

function createArray<T> (length: number, value: T): T[] {
    const arr = Array<T>(length).fill(value)
    return arr
}

const res = createArray<number>(3, 100)
const res1 = createArray<string>(3, '100')
  • Summary:
    • In fact, generics is to change a type that cannot be defined explicitly into a parameter, which we can pass when using

Type declaration

  • In the actual project development, we will certainly use some third-party npm modules, which are not necessarily written through TS, so the members it provides will not have a strong type of experience
  • To put it bluntly, when a member is defined, there is no clear type for some reason, and then we make a separate clear declaration for him when we use it. The reason for this usage is to consider compatibility with some ordinary JS modules. Because the TS community is very powerful, the vast majority of commonly used npm modules have provided corresponding declarations, We only need to install the corresponding type declaration module
  • If there is no corresponding declaration, we can only declare the corresponding module type through the declare statement
import {camelCase} from 'lodash' 

declare function camelCase (input: string): string

const res = camelCase('hello typed')

Tags: TypeScript

Posted by CorkyMcDoogle on Mon, 16 May 2022 00:42:48 +0300