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
- Strongly typed language
It is not allowed to change the data type of a variable unless a cast is performedCopy the code
- Weakly typed language
Variables can be assigned different data typesCopy the code
3. Type Basics: Dynamic and static types
- Statically typed language
* Determine the types of all variables at compile timeCopy the code
- 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
- Create a project folder
- Initialize the project
npm init -y
- Install typeScript globally
npm i typescript -g
- Viewing Help Information
tsc -h
You can see that the compiler has a lot of configuration items - A configuration file is used to configure configuration items
tsc --init
, creates a tsconfig.json file - Create a SRC folder, created in SRC
index.ts
File, write a TS - Use TSC plus the address of the file to compile
tsc ./src/index.ts
After compiling, a JS file with the same name will be generated in the same directory - Install webPack and get the project running
npm i webpack webpack-cli webpack-dev-server -D
- 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
- Ts-loader must be installed during the configuration
npm i ts-loader typescript -D
You need to install typescript locally again - 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
- Write the template file SRC/TPL /index.html
- Ts-loader must be installed during the configuration
- Webpack.dev.config.js is the configuration of the development environment
- Webpack.pro.config.js is a production environment configuration
- Install CleanWebpackPlugin
npm i clean-webpack-plugin -D
- Install CleanWebpackPlugin
- Webpack.config.js is the entry point for all configuration files
- Install the WebPack-merge plug-in to merge the two configuration files
npm i webpack-merge -D
- Install the WebPack-merge plug-in to merge the two configuration files
- Modify NPM script to open package.json file
- Change the entry file to change the path of main to
./src/index.ts
- Modify the scripts
"start": "webpack-dev-server --mode=development --config ./build/webpack.config.js",
- use
npm start
Run the project - Write scripts to build the production environment
"build": "webpack --mode=production --config ./build/webpack.config.js",
- use
npm run build
Running 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:
- 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:
- Poor readability: Difficulty remembering the meaning of numbers
- Poor maintainability: hard coding, pull the whole body
- Question:
-
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)
- Constants (value at compile 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
- Functions and classes can easily support multiple types, increasing the extensibility of programs
- No need to write multiple function overloading, lengthy joint type declaration, enhance code readability
- 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
- Basic type inference
- Best generic type inference
- 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 combined
Document
:
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
- because
haceMuscles
It doesn’t export, it justanimalsHaveMuscles
Functions that share the original namespace for the merge can access this variable.doAnimalsHaveMuscles
Functions 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 above
Merge namespace
The rules described in section are consistent and we must derive themAlnumLabel
Class 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 does
Observable.prototype.map
Know 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 use
import/export
The 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 used
extends
It USESimplements
. Treat the class as an interface and use onlyDisposable
andActivatable
Type 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.
- The first thing you should notice is that it’s not used
- 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
- 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.
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
--out
oroutFile
It 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 in
tsconfig.json
In thefiles
Files 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
--noResolve
Compiler options, triple slash references are ignored; They do not add new files or change the order of a given file.
- If you specify
/// <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 the
import
Module names are resolved similarly in statements. You can simply think of the tri-slash type reference directive asimport
Declared package. - For example, will be
/// <reference types="node"/>
Import to the declaration file to indicate that the file is used@types/node/index.d.ts
The name declared inside; Also, this package needs to be included with the declaration file at compile time. - Only when you need to write one
d.ts
File 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
.ts
A pair is declared in the document@types
Package dependency, use--types
Command line options or intsconfig.json
In the specified. To check intsconfig.json
In the use of@types,typeRoots
andtypes
Learn more.
/// <reference no-default-lib="true"/>
- This directive marks a file as the default library. Will you be in
lib.d.ts
See 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.g
lib.d.ts
). This is the same as using it on the command line--noLib
Similar. - Also notice when passing
--skipDefaultLibCheck
The 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 as
r.js
. amd-module
Specifies that the compiler is allowed to pass an optional module name:
// amdModule.ts
///<amd-module name='NamedModule'/>
export class C {}Copy the code
- This will
NamedModule
Introduced to AMDdefine
Function:
// 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, use
import "moduleName";
Syntax substitution. /// <amd-dependency path="x" />
Tell the compiler there is a nonTypeScript
Module dependencies need to be injected as the target modulerequire
Part of the call.amd-dependency
Instructions can also come with an optionalname
Properties; 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