typeScript

Chapter one: The Basics

1. Reshape “Type thinking”

  • What is typeScript?

    It is a superset of Javascript with a type system

  • Three points to note
    • Type checking (strict static type checking is done at compile time)
    • Language extensions (typescript will include features from ES6 and future proposals, such as decorators for asynchronous operations, as well as features from other languages, such as interfaces and abstract classes)
    • Tool attribute
  • Why use TypeScript?
  • What is the idea of content design for this course?

2. Type Basics: Strong and Weak types

  1. Strongly typed language
It is not allowed to change the data type of a variable unless a cast is performedCopy the code
  1. Weakly typed language
Variables can be assigned different data typesCopy the code

3. Type Basics: Dynamic and static types

  1. Statically typed language
* Determine the types of all variables at compile timeCopy the code
  1. Dynamically typed language
* Determine the types of all variables during executionCopy the code
  • Static type compared with dynamic type static type dynamic type | | | | : – | : – : | – : | | to type extremely strict | very loose type | | the discovery of the error immediately | bugs may hide for months or even years | | runtime performance good | poor runtime performance | | since the document | | poor readability
  • Supporters of dynamically typed languages argue that:
    • Performance can be improved (V8 engine) and language flexibility is more important
    • Hidden errors can be found by unit testing
    • Documentation can be generated by tools
  • conclusion
    • Strongly typed, dynamically typed: Python
    • Weak typing, dynamic typing: Javascript, PHP
    • Strongly typed, statically typed: Java, C#
    • Static type: C, C++

4. Write your first TypeScript program

  1. Create a project folder
  2. Initialize the projectnpm init -y
  3. Install typeScript globallynpm i typescript -g
  4. Viewing Help Informationtsc -hYou can see that the compiler has a lot of configuration items
  5. A configuration file is used to configure configuration itemstsc --init, creates a tsconfig.json file
  6. Create a SRC folder, created in SRCindex.tsFile, write a TS
  7. Use TSC plus the address of the file to compiletsc ./src/index.tsAfter compiling, a JS file with the same name will be generated in the same directory
  8. Install webPack and get the project runningnpm i webpack webpack-cli webpack-dev-server -D
  9. When configuring WebPack, we need to distinguish between the development environment and production environment, because the configuration of the two environments is different. For the sake of project maintainability, we can write the configuration of development environment, production environment and common configuration separately, and finally combine them through plug-ins
  • Create a build directory in your project that holds all the configuration files
  • Webpack.base.config.js is the configuration of the common environment
    1. Ts-loader must be installed during the configurationnpm i ts-loader typescript -DYou need to install typescript locally again
    2. The HtmlWebpackPlugin uses a template to help us generate the front page of a website and automatically embed the output file in this file npm i html-webpack-plugin -D
    3. Write the template file SRC/TPL /index.html
  • Webpack.dev.config.js is the configuration of the development environment
  • Webpack.pro.config.js is a production environment configuration
    1. Install CleanWebpackPluginnpm i clean-webpack-plugin -D
  • Webpack.config.js is the entry point for all configuration files
    1. Install the WebPack-merge plug-in to merge the two configuration filesnpm i webpack-merge -D
  1. Modify NPM script to open package.json file
  2. Change the entry file to change the path of main to./src/index.ts
  3. Modify the scripts"start": "webpack-dev-server --mode=development --config ./build/webpack.config.js",
  4. usenpm startRun the project
  5. Write scripts to build the production environment"build": "webpack --mode=production --config ./build/webpack.config.js",
  6. usenpm run buildRunning the production environment will generate a dist directory and embed the build JS file in our index.html

5. Basic types

The data type of the | | the data type of the ES6 TypeScript | | : – | : – : | – : | | Boolean | Boolean | | Number | Number | | String | String | | Array | Array | | Function | Function | | Object | Object | | Symbol | Symbol | | undefined | undefined | | null | null | | | void | | | any | | | never | | | | yuan group Enumeration | | | | | | advanced type

  • Type annotations
    • Function: Equivalent to type declarations in strongly typed languages
    • Syntax: (variable/function) :type
      • Example:let hello : string = 'Hello typescript'
  • Example:
      // Primitive type
      let bool: boolean = true
      let num: number = 123
      let str: string = 'abc'
    
      / / array
      let arr1: number[] = [1.2.3]
      let arr2: Array<number> = [1.2.3]
      let arr3: Array<number | string> = [1.2.'3']
    
      // tuples (limit the number and type of arrays)
      let tuple: [number.string] = [0.'1']
      // We can push new elements into a tuple, but we can't allow out-of-bounds access
      tuple.push(2)
      console.log(tuple) / / [0, '1', 2]
      // tuple[2] but we can't access it
    
    
      / / function
      let add = (x: number.y: number) :number= > x + y
      let compute: (x: number, y: number) = > number
      compute = (a, b) = > a + b
    
      / / object
      let obj1: object = {x: 1.y: 2}
      // obj1.x = 3 // Direct copy will not work without specifying type
    
      let obj2: {x: number.y:number} = {x: 1.y: 2}
      obj2.x = 3
    
    
      // symbol
      let  s1: symbol = Symbol(a)let s2 = Symbol(a)// undefined, null
      let un: undefined = undefined
      let nu: null = null
    
      // void
      let noReturn = () = > {}
    
      // any(if no type is specified, it can be assigned arbitrarily)
      let x 
      
      // never(a type that never returns a value)
      let error = () = > {
        throw new Error('error')}let endless = () = > {
        while(true){}
      }
    Copy the code

6. Enumeration types

  • An example of role judgment

    function initByRole(role){
      if(role === 1 || role === 2){
    
      } else if (role === 3 || role === 4){
    
      } else if(role === 5 ){
    
      } else{}}Copy the code
    • Question:
      1. Poor readability: Difficulty remembering the meaning of numbers
      2. Poor maintainability: hard coding, pull the whole body
  • Enumeration: A collection of constants by name

    • The value of an enumerator is a read-only value and cannot be modified
    • Classification of enumerators:
      • Constants (value at compile time)
        • There is no initial value
        • A reference to an existing enumerator
        • Some common expressions
      • The number of members that need to be evaluated, the number of expressions that need to be evaluated (value only at program execution time)
  • Example of modifying role judgments using enumerations

     const enum RoleEnum{
        Reporter = 1,
        Developer,
        Maintainer,
        Owner,
        Guest
    }
    
    function initByRole(role: RoleEnum) {
        if (role === RoleEnum.Reporter || role === RoleEnum.Developer) {
            // do sth
        } else if (role === RoleEnum.Maintainer || role === RoleEnum.Owner) {
            // do sth
        } else if (role === RoleEnum.Guest) {
            // do sth
        } else {
            // do sth}}Copy the code

7. Interface: An interface of the object type

  • Interfaces can be used to constrain the structure and types of objects, functions, and classes
  • With reference to obj_interface. Ts
      // interface List {
      // id: number;
      // name: string
      // }
    
      // interface Result {
      // data: List[]
      // }
      // function render(result: Result){
      // result.data.forEach((value) => {
      // console.log(value.id, value.name)
      / /})
      // }
      // let result = {
      // data: [
      // { id: 1, name: 'A'},
      // { id: 2, name: 'B'}
      / /]
      // }
      // render(result)
    
    
    
    
    
      // When the field passed by the background is outside the convention
      // interface List {
      // id: number;
      // name: string
      // }
    
      // interface Result {
      // data: List[]
      // }
      // function render(result: Result){
      // result.data.forEach((value) => {
      // console.log(value.id, value.name)
      / /})
      // }
      // // If we add a field in result that is not defined, it will compile normally
      // let result = {
      // data: [
      // { id: 1, name: 'A', sex: 'male'},
      // { id: 2, name: 'B'}
      / /]
      // }
      // render(result)
    
      // // But when we pass object literals directly, TS checks for additional fields
      // render({
      // data: [
      // { id: 1, name: 'A', sex: 'male'},
      // { id: 2, name: 'B'}
      / /]
      // })
      // // There are three ways to bypass this check
      // // 1. As previously written, we assign object literals directly to a variable
      // // 2. Use type assertions (meaning that this object is a Result)
      // render({
      // data: [
      // { id: 1, name: 'A', sex: 'male'},
      // { id: 2, name: 'B'}
      / /]
      // } as Result)
      // // uses type assertions, the same as the second, but with ambiguity in React
      // render(
            
             {
            
      // data: [
      // { id: 1, name: 'A', sex: 'male'},
      // { id: 2, name: 'B'}
      / /]
      / /})
      // // 3. Use string index signatures
      // interface List{
      // id: number;
      // name: string;
      // [x: string]: any;
      / /}
    
    
    
    
    
    
    
    
    
      // Suppose we need to determine whether the list has an age attribute
      interface List {
        readonly id: number;  // Read-only property, cannot be modifiedname: string; age? : number// Add? To age Represents an optional attribute that can be passed or not passed
      }
    
      interface Result {
        data: List[]
      }
      function render(result: Result){
        result.data.forEach((value) = > {
          console.log(value.id, value.name)
          if(value.age){
            console.log(value.age)
          }
        })
      }
      let result = {
        data: [{id: 1.name: 'A'},
          { id: 2.name: 'B'}
        ]
      }
      render(result)
    
    
      // We can use indexable interfaces when we are not sure how many attributes there are in the interface
      If we index StringArray with any number, we will get a string.
      interface StringArray {
        [index: number]: string
      }
      let chars: StringArray = ['A'.'B']
      // 2. String indexing interface (meaning that indexing StringArray with any string yields a string)
      interface Names {
        [x: string]: string
      }
      // Mix the two
      interface two {
        [x: string]: string;
        [y: number]: string;
      }
    
    Copy the code

8. Interface: Interface of function type

  • With reference to fun_interface. Ts
      You can use variables to define the type of a function
      let addone: (x: number, y: number) = > number
    
      // Use the interface to define it
      interface Add1 {
        (x: number, y: number): number
      }
    
      // Use the type alias
      type Add2 = (x: number, y: number) = > number
    
      let add2: Add2 = (a,b) = > a + b
    
      // Mixed type interface (can define a function or have properties and methods like objects)
      // Define a class library using a mixed-type interface
      interface Lib {
        (): void; // No argument, no return value
        version: string;
        doSomething(): void;
      }
      // Implement a hybrid interface
      let lib: Lib = (() = > {}) as Lib;
      lib.version = '1.0';
      lib.doSomething = () = > {}
      // Wrap it with a function
      function getLib(){
        let lib: Lib = (() = > {}) as Lib;
        lib.version = '1.0';
        lib.doSomething = () = > {}
        return lib;
      }
      // Create one or more instances
      let lib1 = getLib();
      lib1();
      lib1.doSomething();
      let lib2 = getLib();
    Copy the code

9. Comb the relevant knowledge points of functions

  • With reference to the function. The ts
      // Function definition
      function add1(x:number, y:number){
        return x + y
      }
      let add3: (x: number, y: number) = > number
    
      type add4 = (x: number, y: number) = > number
    
      interface add5 {
        (x: number, y: number): number
      }
    
    
      // the number of add1(1,2) parameters cannot be less than or equal to
    
      // Define a function with optional arguments (optional arguments must come after required arguments)
      function add6(x:number, y? :number){
        return y? x+y : x
      }
      add6(5)
      add6(5.6)
    
    
      // Define default values for parameters as in ES6
      function add7(x: number, y = 0,z : number, q = 1){
        return x + y + z + q
      }
      add7(5.undefined.8)  //14, mandatory parameter must be preceded by a default parameter must be passed undefined, after which can not pass
    
    
      // Use the remaining parameters when the parameters are uncertain
      function add8(x: number,... rest: number[]){
        return x + rest.reduce((pre, cur) = > pre+cur)
      }
      add8(5.4.2.3)
    
    
      // Function overloading (when compiling an overload, the compiler looks up the list of previously defined definitions, and uses the definitions that match when they match, so that the most easily matched ones are first)
      function add9(. rest: number[]) :number;
      function add9(. rest: string[]) :string;
      function add9(. rest: any[]) :any{
        let first = rest[0];
        if(typeof first === 'string') {return rest.join(' ')}if(typeof first === 'number') {return rest.reduce((pre, cur) = > pre + cur)
        }
      }
    
    Copy the code

10. Class (1) : Inheritance and member modifiers

  • With reference to the class. Ts
      // class Dog{
      // constructor(name: string){
      // this.name = name
      / /}
      // Public name: string // Common member modifiers
      // run(){}
      // private pri(){} // private member that can be called only by the class, but not by instance or subclass. Adding a private modifier to constructor means that the class cannot be instantiated or inherited
      // Protected pro(){} // Protected members can only be used in classes and subclasses, not in instances of classes. Adding protected to constructor means that the class cannot be instantiated, only inherited
      // readonly legs: number = 4 // This property cannot be changed
      // Static food: string = 'bones' // Static members can only be called by class, not by subclass, class static members will also be inherited
      // }
    
      // class Husky extends Dog{
      // constructor(name: string, color: string){
      // super(name);
      // this.color = color;
      / /}
      // color: string
      // }
    Copy the code

11. Classes (2) : Abstract classes and polymorphism

  • With reference to the class. Ts
      / / abstract classes
      abstract class Animal{
        eat(){
          console.log('eat')}// Abstract methods (implement methods in subclasses)
        abstract sleep(): void
      }
      // let animal = new animal () Abstract class cannot create instance of class, can only be inherited
    
      class Dog extends Animal{
        constructor(name: string){
          super(a)this.name = name
        }
        name: string 
        run(){}
        // override abstract methods
        sleep(){
          console.log("dog sleep")}}let dog = new Dog("wangwang")
      dog.eat();
    
    
      / / ts polymorphism
      class Cat extends Animal{
        sleep() {
          console.log("cat sleep")}}let cat = new Cat();
      let animals: Animal[] = [dog, cat]
      animals.forEach(i= > {
        i.sleep() // dog sleep cat sleep
      })
    
      // This. Member methods of a class can return this directly, facilitating chain calls
      class WorkFlow {
        step1(){
          return this;
        }
        step2(){
          return this; }}new WorkFlow().step1().step2()
      // This is also polymorphic during inheritance, ensuring continuity of interface calls between the parent and subclasses
      class Myflow extends WorkFlow{
        next(){
          return this; }}new Myflow().next().step1().next().step2()
    Copy the code

12. The relationship between classes and interfaces

  • With reference to the class – interface. Ts
      // Class type interface (an interface can constrain what attributes class members have and their types)
      interface Human {
        // The new (name: string): void interface cannot constrain class constructors
        name: string;
        eat(): void;
      }
      // Implement the Huamn interface with Asian,
      // 1. A class implementing an interface must implement all properties declared in the interface
      // 2. Interfaces can constrain only public members of a class
      // 3. Interfaces cannot constrain class constructors
      class Asian implements Human {
        constructor(name: string){
          this.name = name;
        }
        name: string
        eat(){}
        sleep(){} // You can define the class's own attributes
      }
    
      // Interface inheritance
      interface Man extends Human{
        run(): void
      }
    
      interface Child{
        cry(): void
      }
    
      interface Boy extends Man, Child{}
    
      let boy: Boy = {
        name: ' '.run(){},
        eat(){},
        cry(){}}// The interface inherits the class (i.e. the interface abstracts the members of the class, i.e., only the member structure of the class, no concrete implementation)
      class Auto{
        state = 1
        // private state2 = 0
      }
      // The interface contains attributes of the class
      interface AutoInterface extends Auto {
    
      }
      // To implement the AutoInterface, you only need the member to have the state attribute
      class C implements AutoInterface{
        state = 1
      }
    
      // Subclasses of Auto can also implement AutoInterface
      // There is no need to add the state attribute to Bus, because it is a subclass of Auto
      // The interface removes not only the public member, but also the private member and the protected member
      class Bus extends Auto implements AutoInterface {}Copy the code

13. Generics (1) : Generic functions and generic interfaces

  • Generics: Data types that are not defined in advance. The specific type is determined at the time of use.
  • Many times we need a single function or class that can support multiple data types
  • With reference to generics. Ts
 // A print function that can only accept strings
 function log1(value: string) :string {
   console.log(value);
   return value
 }
 // We want this function to take an array of strings
 // 1. Overload by function
 function log2(value: string) :string;
 function log2(value: string[]) :string[];
 function log2(value: any){
   console.log(value);
   return value
 }
 // 2. Union type
 function log3(value: string | string[]) :string | string[]{
   console.log(value);
   return value;
 }
 // 3. Type any (any loses the constraint between types, ignoring that the type of the input parameter and the type of the return parameter must be the same)
 function log(value: any) :any{
   console.log(value);
   return value;
 }

 // Generics can be used because of any
 // Generics: data types are not predetermined. The specific type is determined at the time of use.
 // The type T in the log function does not need to be specified in advance. On the other hand, the consistency of the input and return values is guaranteed
 function log4<T> (value: T) :T {
   console.log(value);
   return value
 }
 // Call the type of T.
 log4<string[]>(['a'.'b'])
 // Call method (2. Use ts type inference, omit type parameters, recommended)
 log(['a'.'b'])


 // You can use generics to define not only a function but also a function type
 // type Log = <T>(value: T) => T
 // let myLog: Log = log

 // Generic interface
 // Only one function is constrained
 interface Log {
   <T>(value: T): T
 }
 You can use the following methods to constrain other members of the interface
 interface Log1<T>{
   (value: T): T
 }
 // We must specify a type when implementing generic variables that constrain the entire interface
 let myLog: Log1<number> = log
 myLog(1)
 // Specify a type directly during interface definition
 // interface Log1<T=string>{
 // (value: T): T
 // }
 // let myLog: Log1 = log
 // myLog("ss")
Copy the code

Generics (2): Generic classes and generic constraints

  • With reference to generics. Ts
  • Benefits of generics
  1. Functions and classes can easily support multiple types, increasing the extensibility of programs
  2. No need to write multiple function overloading, lengthy joint type declaration, enhance code readability
  3. Flexible control of constraints between types
  // 14. Generics constrain class members
  class LogX<T>{
    run(value: T){
      console.log(value)
      return value
    }
    // Generics cannot be applied to static members of a class
    // static jing(value: T){
    // return value
    // }
  }
  // If the type can be specified in the instance, the type must be specified in the call
  let logx = new LogX<number>()
  logx.run(1)
  // If the type is not specified, a value of any type can be passed
  let logx1 = new LogX()
  logx1.run({s:'s'})

  // Generic constraints
  interface Length{
    length: number
  }
  // Let T inherit the Length interface, then T will be constrained, can not be arbitrary type, input parameter no matter what type must have lenth attribute
  function logy<T extends Length> (value: T) :T{
    console.log(value, value.length)
    return value
  }
  // The input parameter must have the Lenth attribute regardless of type
  logy([2])
  logy('123')
  logy({length: 1})
Copy the code

15. Type checking mechanism (1): Type inference

  • Type checking mechanism:

    Some of the principles and behaviors the TypeScript compiler adheres to when doing type checking

    Role: assist development, improve development efficiency

  • Type inference

    Instead of specifying the type of a variable (the return value type of a function),TypeScript can automatically infer a type for it based on certain rules

  1. Basic type inference
  2. Best generic type inference
  3. Context type inference
  • With reference to the ts
 // 1. Base type inference
 // 1.1 Initializes a variable
 let a  // ts will be inferred to be any
 let b = 1 // ts will be inferred as number
 let c = 'jing' // ts will infer to string
 let d = [] // ts is inferred to be an array of type any
 let g = [1] // ts will infer an array of type number
 / /...

 // 1.2 When setting function default parameters
 let k = (x =1) = > {}  / / x for the number
 let l = (x = 1) = > x + 1 / / l number


 When inferring a type from multiple types, TS tries to infer a common type that is compatible with all current types
 // 2. Best generic type inference
 let m = [1.null]  // Is inferred to be the union type of number and NULL

 // Both are inferred from right to left, and from left to right
 // 3. Context type inference (usually in event processing)
 // The event is inferred as a keyboard event attribute
 window.onkeydown = (event:KeyboardEvent ) = > {
   console.log(event.BUBBLING_PHASE)
 }


 // When ts' type inference does not match our expectations, TS provides methods that allow it to override its inference, called type assertion
 interface Foo {
   bar: number
 }
 let foo = {} as Foo  // With this assertion, foo.bar = 1 is removed without error, but the bar property is omitted
 // foo.bar = 1
 // We specify its type when declaring because it will be omitted
 // If you do not add the bar attribute, an error will be reported
 let foo1: Foo ={
   bar: 1
 }
Copy the code

A statement to merge

Declaration merging is when the compiler merges multiple independent declarations for the same name into a single declaration. The merged declaration has the same features as the original declaration.

Combined interface

  • The simplest and most common type of declared merge is interface merge. The mechanism for merging is to put the members of both parties in an interface of the same name.
  interface Box {
    height: number;
    width: number;
  }
  interface Box {
    scale: number;
  }
  let box: Box = {
    height: 5.width: 6.scale: 10
  };
Copy the code
  • Non-function members of an interface should be unique. If not unique, then they must be of the same type. If two interfaces declare non-function members of the same name and their types are different, the compiler will report an error.

  • For function members, each function declaration with the same name is treated as an overload of that function. Note that after interface A is merged with subsequent interface A, the subsequent interface has A higher priority.

    • As shown in the following example:
      interface Cloner {
        clone(animal: Animal): Animal;
      }
      interface Cloner {
        clone(animal: Sheep): Sheep;
      }
      interface Cloner {
        clone(animal: Dog): Dog;
        clone(animal: Cat): Cat;
      }
    Copy the code
    • The three interfaces above are combined into one declaration:
      interface Cloner {
        clone(animal: Dog): Dog;
        clone(animal: Cat): Cat;
        clone(animal: Sheep): Sheep;
        clone(animal: Animal): Animal;
      }
    Copy the code
    • Note that the order of declarations in each set of interfaces remains the same, but the order between interfaces is that later interface reloads appear at the front.
    • One exception to this rule is when a special function signature occurs. If one of the parameters in the signature is of a single literal type (such as a love song type that is not a string literal), it is promoted to the top of the overload list.
      • As shown in the following example
        interface Document {
          createElement(tagName: any): Element;
        }
        interface Document {
          createElement(tagName: "div"): HTMLDivElement;
          createElement(tagName: "span"): HTMLDivElement;
        }
        interface Document {
          createElement(tagName: string): HTMLDivElement;
          createElement(tagName: "canvas"): HTMLCanvasElement;
        }
      Copy the code
      • The combinedDocument:
        interface Document {
          createElement(tagName: "canvas"): HTMLCanvasElement;
          createElement(tagName: "div"): HTMLDivElement;
          createElement(tagName: "span"): HTMLSpanElement;
          createElement(tagName: string): HTMLElement;
          createElement(tagName: any): Element;
        }
      Copy the code

Merge namespace

  • Like interfaces, namespaces with the same name merge their members. Namespaces and values are shown in the namespace venue.
  • For the merging of namespaces, the interfaces exported by modules with the same name are merged to form a single namespace containing the merged interfaces.
  • For merging values in a namespace, if a namespace with the given name already exists, the exported members of the subsequent namespace are added to the module that already exists.
    • Animals declaration merge example (merge of exported members) :
      namespace Animals {
        export class Zebra {}}namespace Animals {
        export interface Legged { numberOfLegs: number; }
        export calss Dog {}
      }
      / / after the merger
      namespace Animals {
        export interface Legged { numberOfLegs: number; }
        export class Zebra {}
        export class Dog {}}Copy the code
    • Merge of non-exported members
      • Non-exported members are visible only in their original (pre-merge) namespace. This means that after the merge, members merged from other namespaces cannot access non-exported members.
        namespace Animal {
          let haveMuscles = true;
          export function animalsHaveMuscles() {
            returnhaveMuscles; }}namespace Animal {
          export function doAnimalsHaveMuscles() {
            return haveMuscles; // Error, because haveMuscles is not accessible here}}Copy the code
      • becausehaceMusclesIt doesn’t export, it justanimalsHaveMusclesFunctions that share the original namespace for the merge can access this variable.doAnimalsHaveMusclesFunctions that are part of the merge namespace cannot access unexported members.

Namespaces are merged with class and function and enumeration types

  • Namespaces can be merged with other declarations. As long as the definition of the namespace matches the definition of the type to be merged. The merge result contains the declaration types for both.
Merge namespaces and classes
  • This allows us to represent inner classes.
  class Album {
    label: Album.AlbumLabel;
  }
  namespace Album {
    export class AlbumLabel {}}Copy the code
  • Merge rules with aboveMerge namespaceThe rules described in section are consistent and we must derive themAlnumLabelClass so that the merged class can access it. The result of the merge is a class with an inner class. You can also use namespaces to add static attributes to classes.
  • In addition to the inner class pattern, it’s not uncommon in JavaScript for you to create a function and then extend it to add some properties. TypeScript uses declarative merging for this purpose and for type safety.
  function buildLable(name: string) :string {
    return buildLable.prefix + name + buildLabel.suffix;
  }
  namespace buildLable {
    export let suffix = "";
    export let prefix = "Hello, ";
  }
  console.log(buildLabel("Sam Smith"))
Copy the code
  • Namespaces can be used to extend enumerated types:
  enum Color {
    red = 1,
    green = 2,
    blue = 4
  }
  namespace Color {
    export function mixColor(colorName: string) {
      if(colorName == 'yellow') {
        return Color.red + Color.green;
      }else if(colorName == 'white') {
        return Color.red + Color.green + Color.blue;
      }else if(colorName == 'magenta') {
        return Color.red + Color.blue;
      }else if(colorName =='cyan') {
        return Color.green + Color.blue
      }
    }
  }
Copy the code

Illegal merger

  • Note: TypeScript does not allow all merges. Currently, classes cannot be merged with other classes or variables, as will be mentioned later in the documentation. See TypeScript mixin

The module development

  • Although JavaScript does not support merging, you can patch imported objects to update them.
  // observable.js
  export class Observable<T> {
    / /...
  }
  // map.js
  import { Observable } from './observable' 
  Observable.prototype.map = function(f) {
    / /...
  }
Copy the code
  • It also works well in Typescript, but the compiler doesObservable.prototype.mapKnow nothing. You can use the extension module to tell the compiler:
  // observable.ts stays the same
  // map.ts
  import { Observable } from "./observable";
  declare module "./observable" {
    interface Observable<T> {
      map<U>(f: (x: T) = > U): observable<U>;
    }
  }
  Observable.prototype.map = function (f){
    // ...
  }

  // consumer.ts
  import { Observable } from "./observable";
  import "./map";
  let o: Observable<number>;
  o.map(x= > x.toFixed());
Copy the code
  • Module name resolution and useimport/exportThe way module identifiers are resolved is consistent. When these declarations are merged in the extension, they are declared as if they had been declared in the original location. However, you can no longer extend the total declaration to new top-level declarations – only declarations that already exist in the module can be extended.

The global extension

  • You can also add declarations inside modules to the global scope.
  // observable.ts
  export class Observable<T> {
    / /...
  }
  declare global {
    interfaceArray<T> { toObservable(): Observable<T>; }}Array.prototype.toObservable = function() {
    // ...
  }
Copy the code
  • The behavior and limitations of global extension and module extension are the same.

A decorator

  • With the introduction of classes in TypeScript and Es6, there are some scenarios where we need additional features to support annotation or modification of classes and their members. Decorators provide a way for us to add annotations to class declarations and members through metaprogramming syntax. Decorators in JavaScript are currently in the second stage of the solicitation for proposals.

Mixins

  • In addition to the traditional object-oriented inheritance approach, a popular way to create classes from reusable components is to combine the code of another simple class.

With the sample

  // Mix once
  class Disposable {
    isDisposed: boolean;
    dispose() {
      this.isDisposed = true; }}// Active mix
  class Activatable {
    isActive: boolean;
    activate() {
      this.isActive = true;
    }
    deactivate() {
      this.isActive = false; }}class SmartObject implements Disposable.Activatable {
    constructor() {
      setInterval(() = > console.log(this.isActive +":"+this.isDisposed),500)}interact() {
      this.activate();
    }
    // Disposable
    isDisposed: boolean =  false;
    dispose: () = > void;
    // Activatable
    isActive: boolean = false;
    activate: () = > void;
    deactivate:() = > void;
  }
  applyMixins(SmartObject, [Disposable, Activatable]);

  let smartObj =  new SmartObject();
  setTimeout(() = > smartObj.interact(),1000)

  // In your runtime library somewhere

  function applyMixins(derivedCtor: any, baseCtors: any[]){
    baseCtors.forEach(baseCtor= > {
      Object.getOwnPropertyNames(baseCtor.portotype).forEach(name= >{ derivedCtor.prototype[name] = baseCtor.prototype[name]; })})}Copy the code
  • Understand this example
    • The code starts by defining two classes that will act as mixins. You can see that each class defines only one specific behavior or function. We’ll use them later to create a new class with both capabilities.
      // Disposable Mixin
      class Disposable {
          isDisposed: boolean;
          dispose() {
              this.isDisposed = true; }}// Activatable Mixin
      class Activatable {
          isActive: boolean;
          activate() {
              this.isActive = true;
          }
          deactivate() {
              this.isActive = false; }}Copy the code
    • Let’s create a class that combines these two mixins.
      class SmartObject implements Disposable.Activatable{}
      Copy the code
      • The first thing you should notice is that it’s not usedextendsIt USESimplements. Treat the class as an interface and use onlyDisposableandActivatableType rather than its implementation. This means that we need to implement interfaces in our classes. But that’s what we want to avoid when we use mixins.
    • We can do this by creating placeholder properties for the property methods that will be mixin. This tells the compiler that these members are available at run time. This allows you to take advantage of mixins, although some placeholder attributes need to be defined in advance.
        // Disposable
        isDisposed: boolean = false;
        dispose: () = > void;
        // Activatable
        isActive: boolean = false;
        activate: () = > void;
        deactivate: () = > void;
      Copy the code
    • Finally, mixins are mixed into the defined classes to complete the implementation.
        applyMixins(SmartOject, [Disposable, Activatable]);
      Copy the code
    • And finally create this helper function to help us do the mixin operation. He facilitates all the properties on mixins and copies them to the target, replacing the placeholder properties with the actual implementation code.
      function applyMixins(derivedCtor: any, baseCtros: any[]) {
        baseCtors.forEach(baseCtro= > {
          Object.getOwnPrototypeNames(baseCtor.prototype).forEach(name= >{ derivedCtor.prototype[name] = baseCtor.prototype[name]; })})}Copy the code

Three slash instruction

The triple slash directive is a single-line comment that contains a single XML tag. The contents of the comment are used as compiler instructions.

  • The triple slash instruction can only be placed at the top of the file containing it. Only one or more lines of comments can be preceded by a triple-slash instruction, which contains other triple-slash instructions. If they appear after a statement or declaration, they are treated as plain one-line comments and have no special meaning.

/// <reference path="..." />

  • /// <reference path="..." />The instruction is the most common of the three slash instructions. It is used to declare dependencies between files.
  • The triple slash reference tells the compiler what additional files to introduce in the build project.
  • When using--outoroutFileIt can also be used as a way to adjust the order of output. The position of the files in the output file contents is consistent with the preprocessed input order.
  • Preprocessing file
    • The compiler preprocesses the input file to parse all the tri-slash reference instructions. During this process, additional files are added to the compilation process.
    • The process starts with some root files, which are specified on the command line or intsconfig.jsonIn thefilesFiles in the list. These are preprocessed with the files in the specified order. Before a file is added to the list, all the triple slash references it contains are processed, along with the targets they contain. Tri-slash references are parsed depth-first in the order in which they appear in the file.
    • A triple slash reference path is relative to the containing file, if not the root file.
    • Error: An error is reported when referring to a file that does not exist. A file referencing itself with a triple slash instruction will report an error.
    • use--noResolve
      • If you specify--noResolveCompiler options, triple slash references are ignored; They do not add new files or change the order of a given file.

/// <reference types="..." />

  • with/// <reference types="..." />Directive similar, this directive is used to declare since; a/// <reference types="..." />The directive indicates a dependency on a package.
  • The name of these packages is resolved with theimportModule names are resolved similarly in statements. You can simply think of the tri-slash type reference directive asimportDeclared package.
  • For example, will be/// <reference types="node"/>Import to the declaration file to indicate that the file is used@types/node/index.d.tsThe name declared inside; Also, this package needs to be included with the declaration file at compile time.
  • Only when you need to write oned.tsFile only when using this directive.
  • The compiler automatically adds declaration files that are generated at compile time/// <reference types="..." />; Added to the generated declaration file if and only if the referenced package declaration is used in the result file/// <reference types="..." />Statements.
  • If you want to in.tsA pair is declared in the document@typesPackage dependency, use--typesCommand line options or intsconfig.jsonIn the specified. To check intsconfig.jsonIn the use of@types,typeRootsandtypesLearn more.

/// <reference no-default-lib="true"/>

  • This directive marks a file as the default library. Will you be inlib.d.tsSee this comment at the top of the file and its different variants.
  • This directive tells the compiler not to include the default library during compilation (e.glib.d.ts). This is the same as using it on the command line--noLibSimilar.
  • Also notice when passing--skipDefaultLibCheckThe compiler only ignores the check band/// <reference no-default-lib="true"/>The file.

/// <amd-module />

  • By default, generated AMD modules are anonymous. However, problems arise when some tools need to process generated modules, such asr.js.
  • amd-moduleSpecifies that the compiler is allowed to pass an optional module name:
  // amdModule.ts
  ///<amd-module name='NamedModule'/>
  export class C {}Copy the code
  • This willNamedModuleIntroduced to AMDdefineFunction:
// amdModule.js
define("NamedModule"["require"."exports"].function(require.exports) {
  var C = (function() {
    function C() {}
    return C;
  })
  exports.C = C;
})
Copy the code

/// <amd-dependency />

  • ** Note: ** this command is deprecated, useimport "moduleName";Syntax substitution.
  • /// <amd-dependency path="x" />Tell the compiler there is a nonTypeScriptModule dependencies need to be injected as the target modulerequirePart of the call.
  • amd-dependencyInstructions can also come with an optionalnameProperties; He allowed us to pass in an optional name for AMD-Dependency
  /// <amd-dependency path="legacy/moduleA" name="moduleA"/>
  declare var moduleA:MyType
  moduleA.callStuff()
Copy the code
  • Generated JavaScript code:
  define(["require"."exports"."legacy/moduleA"].function (require.exports, moduleA) {
      moduleA.callStuff()
  });
Copy the code