In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces "what are the knowledge points about TypeScript". In daily operation, I believe many people have doubts about what knowledge points about TypeScript. The editor consulted all kinds of materials and sorted out simple and easy-to-use methods of operation. I hope it will be helpful to answer the questions about "what knowledge points about TypeScript"! Next, please follow the editor to study!
Start TypeScript
Study preparation
Before starting the formal study of TypeScript (hereinafter referred to as TS), it is recommended to make the following preparations:
Node version > 8.0
IDE (recommended VS Code,TS is launched by Microsoft, VS Code is also launched by Microsoft, and lightweight. More friendly to TS code)
First acquaintance of TypeScript
When you open the official website of TypeScript, you can see that the official definition of TS is like this.
JavaScript and More A Result You Can TrustGradual Adoption
These three points are a good interpretation of the characteristics of TypeScript. Before that, let's briefly experience the changes that TypeScript has brought to our programming.
This is a .js file code:
Let a = 123a = '123'
This is the .ts file code:
Let b = 123b = '123'
When we tried to reassign b in the TS file, an error occurred, the mouse moved to the red, and the system prompted:
Type "123"'is not assignable to type 'number'
What is the reason?
The answer is simple: in TS, all variables are static types, and let b = 123 is actually 'let b:number = 123'. B can only be a value of type number and cannot be assigned to other types.
Advantages of TypeScript
TS static types that allow us to find problems in the development process
More friendly editors automatically prompt
The semantics of the code is clear and easy to understand, and the collaboration is more convenient.
With code to feel how intuitive the programming experience is brought by these three advantages, it is recommended to type the code on the editor.
Start with the most familiar JS:
Function add (data) {return data.x + data.y} add () / / when you write this directly, there will be an error to inform add at run time ({XRO 2mlx 3})
The last piece of TS code (if you have any questions about the grammar, you can leave it alone. It will be explained later, and you can start with questions here)
Interface Point {x: number, y: number} function tsAdd (data: Point): number {return data.x + data.y} tsAdd () / / write this directly, and the editor gives an error message tsAdd ({x: 1Cody y: 123})
When we call the attribute in the data variable in TS, the editor will think about the x and y attributes, and we can look directly outside the function and know the attribute value of data without going deep. This is the convenience and efficiency that TS brings to us compared to JS.
TypeScript environment building
To build a TypeScript environment, you can execute commands directly on the terminal:
Npm install-g typescript
Then we can cd directly to the ts folder and run it on the terminal:
Tsc demo.ts
Tsc is simply typescript complaire. Compile the demo.ts, and then you can see that a JS file with the same name has been added to the directory, which can be compiled directly with Node.
At this point, we can run the TS file, but this is just a file, and we have to manually compile it to TS and run Node manually. Is there an one-step command in place? Of course, ts-node is installed on the terminal:
Npm install-g ts-node
So we can run it directly:
Ts-node demo.ts
To run the TS file, if you want to initialize the ts folder and configure TS, you can run:
Tsc-init
With regard to the relevant configuration, we will briefly mention it here. Later, we will analyze the commonly used configuration. You can first open the tsconfig.json file by yourself, simply take a look at the configuration, and then move on with questions.
Understand the Type in TypeScript again.
Before you formally introduce the syntax of TS, you need to make the static types mentioned at the beginning a little clearer.
Const a: number = 123
As mentioned earlier, the code means that an is a constant of type number, and the type cannot be changed. What I'm going to say here is that a has the properties and methods of number, and when we call the properties and methods of an in the editor, the editor will give us the properties and methods of number for us to choose from.
TS allows us to define not only base types for variables, but also custom types:
Interface Point {x: number y: number} const a: Point = {x: 2, y: 3}
By defining an as a Point type, a has the properties and methods of Point. After we define an as the Point type, a must have the x and y attributes on Point. In this way, we have almost understood Type.
Type classification of TypeScript
Analogous to the type of JavaScript, TypeScript is also divided into base type and reference type.
Original type
The primitive types are boolean, number, string, void, undefined, null, symbol, bigint, any, never.
Some in JS are not explained here, mainly talking about several types that have not been seen before, but it should be noted that when we declare TS variable types, they are all lowercase, not uppercase, which is the constructor of the representation.
Void means no type, usually we assign it to a function that does not return a value:
Function voidDemo (): void {console.log ('hello world')}
Bigint can be used to manipulate and store large integers. Even if this number has exceeded the range of safe integers that can be represented by the JavaScript constructor Number, it is rarely used in real situations, and students who are interested can study it by themselves.
Any refers to any type, and in actual development, you should try not to define objects as any types:
Let a: any = 4 a ='4'
Never represents a type of value that never exists, and the most common is a situation in a function that does not execute to the end:
Function error (message: string): never {throw new Error (message) console.log ('never execute')} function errorEmitter (): never {while (true) {}}
Reference type
Object types: when assigning values, there must be defined object properties and methods
Const person: {name: string age: number} = {name: 'aaa' age: 18}
Array type: each item in the array is a defined type.
Const numbers: number [] = [1,2,3]
Class type: you can ignore the writing first, which will be explained in detail later.
Class Peron {} const person: Person = new Person ()
Type of introduction is almost so much knowledge, first have an impression in mind, do not understand the place can continue to look down with questions.
TypeScript type annotation and inference
I have already talked about the types of TypeScript and its types, but this section still wants to continue to cover all the knowledge about types, that is, type annotations and type inference.
Type Note let a: number a = 123
This is the type annotation in the above code, which tells TS the type of variable by explicitly declaring it:
Let b = 123
Here we do not explicitly declare the type of b, but we place the cursor over b in the editor, and the editor will tell us its type. This is type inference.
In simple cases, TS can automatically analyze types, but in complex cases, TS cannot analyze variable types, so we need to use type annotations.
/ / scene 1 function add (first,second) {return first + second} const sum = add (1Magne2) / / scene 2 function add2 (first: nnumber,second: number) {return first + second} const sum2 = add2 (1Magne2)
In scenario 1, the type TS of formal parameters first and second is inferred to any, and the return value of the function is also inferred to be any, because in this case, TS cannot determine the type, and number or string may be passed when passing parameters.
In scenario 2, even if we don't define the type of sum2, TS can still infer number, because sum2 is the result of the sum of first second, so it must be number.
Whether it's type inference or type annotation, our goal is to want the type of the variable to be fixed so that it doesn't turn typescript into anyscript.
Supplement: type comments in the function structure.
/ / case 1 function add ({first}: {first: number}): number {return first} / / case 2 function add2 ({first, second}: {first: number, second: number}): number {return first + second} const sum2 = add ({first: 1, second: 2}) const sum2 = add2 ({first: 1, second: 2}) TypeScript Advanced
Configuration file
As we mentioned earlier, when we want to run the TS file, execute the command tsc file name .ts to compile the TS file to generate a JS file with the same name. How does this process come from, or what if we want to change the generated file name and file directory?
I'm sure you already have the answer in mind, and yes, like webpack packaging or babel compilation, TS has a compiled configuration file tsconfig.json. When we execute ts-init, there is an extra TS configuration file in the file directory, and TS is compiled into js, which is configured in tsconfig.
To verify that the tsconfig file does configure the compilation of the TS file, modify the following:
"removeComments": true / / removes comments from the file
Then create a new demo.ts file:
/ / this is a comment const a: number = 123
Execute tsc demo.ts, open the demo.js file, and find that the comments have not been removed, what is the matter, the configuration file does not take effect?
The truth is, when we execute the file directly, we don't use the configuration in tsconfig, only if we execute tsc directly, we use the configuration in tsconfig, run tsc directly, and you'll see, amazing!
When running the tsc command, the tsconfig configuration file will be found first, and if no other changes are made, the TS file in the root directory will be compiled by default.
If you want to compile the specified file, you can add it at the same level as the compilerOptions configuration item:
"include": [. / demo.ts "] or" files ": [. / demo.ts"]
If you want not to include a file, you can add it as above:
"exclude": [". / demo.ts"]
For more configuration on this section, you can refer to the tsconfig configuration documentation.
Next, let's take a look at the properties in compilerOptions, as you can see from this English name, which actually refers to the compiled configuration.
"compilerOptions": {"increments": true / / incremental compilation Compile only the newly added content "target": "es5", / / specify the target version of ECMAScript: 'ES5' "module": "commonjs", / / specify the use module:' commonjs', 'amd',' system', 'umd' or' es2015' "moduleResolution": "node" / / Select module parsing policy "experimentalDecorators": true, / / enable the experimental ES decorator "allowSyntheticDefaultImports": true, / / allow default import from modules that do not have a default export set. "sourceMap": true, / / when compiling a ts file into a js file Also generate the corresponding map file "strict": true, / / enable all strict type checking options "noImplicitAny": true, / / have implied any types in expressions and declarations Times error "alwaysStrict": true, / / check the module in strict mode And add 'use strict' "declaration": true "in each file, / / generate the corresponding .d.ts file" removeComments ": true, / / delete all compiled comments" noImplicitReturns ": true, / / not all return paths of the function have a return value Times error" importHelpers ": true / / Import accessibility functions "lib" from tslib: ["es6", "dom"], / / specify the library files "typeRoots": ["node_modules/@types"], "outDir": ". / dist" to be included in the compilation. / / generate file directory "rootDir": ". / src" / / entry file}, API (interface)
Interfaces are a way of customizing types or translating for our third-party JS library. Interfaces have been used in the previous code, which is actually used to describe types. Everyone has a name and age, so we will restrain person in this way.
Interface Person {name: string age: number} let person: Person
When we implement this type constraint, person must have name and age when initializing the object. There are two ways to initialize it. Let's take a look at the different expenses:
/ / undertake the above code / / the first initialization method person = {name: 'aaa', age: 18} / / the second initialization method let p = {name:' aaa', age: 18, sex: 'male'} person = p
In the first way, compared with the second way, there is an extra sex attribute in the p object, and then the value is assigned to person, and the editor does not prompt for an error, but if you add a sex attribute in the first way, it will report an error. Why?
This is because when we assign values directly (that is, in the first way), TS does strong type checking, so it must be consistent with the type defined by the interface.
Note that we mentioned consistency above, which means that the attribute name and attribute value type are the same, and the number of attributes is no more or less. When the second method is used for assignment, a weak check is performed. The same number of attributes will be weak, as shown in that when there is one more attribute, there will be no syntax errors.
At this point, we will have a question, what if we want the first way to be the same as the second way, that is to say, everyone's age and name are required, but the city city is optional? We can describe it with optional attributes.
Interface Person {name: string age: number city?: string}
If so, we can see that the city property may be string or undefined when we call the p property:
Not only that, we also hope that the age property is immutable, so the readonly property will naturally come in handy, and when you try to modify and define the readonly property, the editor will issue a warning:
Interface Person {name: string readonly age: number city?: string} let person: Person = {name: 'aaa', age: 18} / / person.age = 18
Of course, this is not over, what if one day you want to extend another interface, which is the employee's interface, but the employee interface class must have all the information about the Person class, and what if you extend another id? That's when extends comes on.
Interface Employee extends Person {id: number}
The interface can also be used to constrain a class so that the defined class must have some property or method, so that the keyword is not extends, but implements.
Interface User {name: string getName (): string} class Student implements User {name = 'aaa' getName () {return this.name}}
Interface VS type
The functions of interface and type seem to be similar, both used to define types, so let's take a look at their similarities and differences.
Similarities:
1. Can describe objects or functions
Interface Person {name: string age: number} type Person1 = {/ / type definition type has an equal sign name: string age: number} interface getResult {(value: string): void} type getResult1 = (value: string): void
two。 Can implement inheritance.
/ / interface inherit interface interface People extends Person {sex: string} / / interface inherit typeinterface People extends Person1 {sex: string} / / type inherit type type People1 = Person1 & {sex: string} / / type inherit interface type People1 = Person & {sex: string}
Differences:
1. Type can declare basic types and federated types, but not interface
/ / basic type type Person = string / / Union type type User = Person | number
2. Interface can be merged by type
Interface People {name: string age: number} interface People {sex: string} / / People final type is {name: string age: number sex: string}
3. Interface can define optional and read-only attributes (as mentioned earlier, I will not repeat them here)
The basic knowledge of the interface is almost finished, of course, the application of the interface in the actual development scenario will be more complex, if you still have a lot of doubts, and then read on, the following explanation will answer your questions.
Joint type and type protection
Unlike other sharing materials, I hope that each knowledge point can first make you confused and inspire your thinking, and then I will slowly solve your doubts, so that I believe you will remember more deeply, otherwise you may have little effect.
Talk less and go straight to a piece of code:
Interface Bird {fly: boolean sing: () = > {}} interface Dog {fly: boolean bark: () = > {}} function trainAnimal (animal: Bird | Dog) {/ / animal.sing ()
I defined two types in the above code, one is the Bird type and the other is the Dog type. The formal parameter of the function trainAnimal takes an argument to animal, which may be of type Bird or Dog, which is the union type. When called in a function, the editor is prompted only by fly:
This is really something, but when you think about it, there is nothing wrong with fly. Because the animal of the federated type cannot determine which type it is, it can only prompt for common properties. On the other hand, the unique method cannot be prompted by syntax after being blocked by the joint type. If we forcibly call a method unique to a type, we can see that the editor has an error message.
What if you do need to use a unique method?
This requires type protection, and indeed, if federated types can only call common methods, it doesn't seem to be very useful, but it's good to have type protection. There are also many types of type protection, which we will introduce separately.
1. Type assertion
Function trainAnimal (animal: Bird | Dog) {if (animal.fly) {(animal as Bird) .sing ()} else {(animal as Dog) .bark ()}}
The type assertion is implemented through an as keyword in the above code. Because logically, we know that if there is a fly method, then animal must be a Bird type, but the editor does not know, so tell the editor through as that animal is the Bird type at this time, and the determination of Dog type is the same.
two。 Through in to type assertion, TS syntax checking can determine the parameter type
Function trainAnimalSecond (anmal: Bird | Dog) {if ('sing' in animal) {animal.sing ()}}
3. Type protection through typeof
Function add (first: string | number, second: string | number) {if (typeof first = 'string' | | typeof second = =' string') {return `first:$ {first} second:$ {second} `} return first + second}
How to judge directly without the logic in if in the above code, the editor will give an error, because if it is the addition of numbers and strings, there may be errors, so through typeof to determine, when first and second are both numbers, add.
4. Type protection through instanceof
Class NumberObj {count: number} function addSecond (first: object | NumberObj, second: object | NumberObj) {if (first instanceof NumberObj & & second instanceof NumberObj) {return first.count + second.count}}
In TS, a class can be used not only to instantiate an object, but also to define a variable type. When an object is defined by a class, it indicates that the value of the object is an instance of the class. If you have any questions about the writing of the class, you can check the relevant content of ES7, which will not be explained too much here.
We can see from the code that we use instanceof to determine whether the parameter with the union type is the type of the class, of course, if we want to use instanceof to judge, our custom type definition can only use class. If it is a type defined by interface, an error will be reported using instanceof.
Enumerated type
Enumerate this concept, we have come into contact with more in JS, on the concept does not do too much explanation, directly on a section of code.
Const Status = {OFFLINE: 0, ONLINE: 1, DELETED: 2} function getStatus (status) {if (status = = Status.OFFLINE) {return 'offline'} else if (status = = Status.ONLINE) {return' online'} else if (status = = Status.DELETED) {return 'deleted'} return error}
This is a common way to write it in JS. There are enumerated types in TS, and it is easier to use than JS.
Enum Status {OFFLINE, ONLINE, DELETED} / / Mode 1 const status = Status.OFFLINE / / 0 / / Mode 2 const status = Status [0] / / OFFLINE
As you can see from the above code, TS's enumerated types are assigned by default and are easy to write. Looking at the use of enumerated types in mode 1 and mode 2, we can see that TS enumerated types also support positive and negative calls.
I just mentioned that enumerated types have default values. What if I want to change the default values? Take a look at the following code:
Enum Status {OFFLINE = 3, ONLINE, DELETED} const status = Status.OFFLINE / / 3 const status = Status.ONLINE / / 4 enum Status1 {OFFLINE = 6, ONLINE = 10, DELETED} const status = Status.OFFLINE / / 6 const status = Status.ONLINE / / 10 const status = Status.DELETED / / 11
As can be seen from the above, the TS enumeration type supports custom values, and if the subsequent enumeration properties are not assigned, they will be incremented on the basis of the original.
We mentioned above that enum supports two-way use. Why is it so beautiful and flexible? let's take a look at the code after enumerated types are compiled into JS:
Var Status; (function (Status) {Status [Status ["OFFLINE"] = 6] = "OFFLINE"; Status [Status ["ONLINE"] = 10] = "ONLINE"; Status [Status ["DELETED"] = 12] = "DELETED";}) (Status | (Status = {}))
Function generics
Generics are widely used in TS development, so this section will also go from shallow to deep, looking at the code first:
Function result (first: string | number, second: string | number) {return `${first} + ${second}`} join (1) join (1)
This is the union type we talked about earlier, and the two parameters can be either a number or a string.
But now I have a requirement like this: if first is a string, then second can only be a string, similarly, first is a number, then second. If we don't know generics, we can only make logical conventions within the function, but as soon as generics are done, the problem is easily solved.
Function result (first: t first: t) {return `${first} + ${second}`} join (1m 1) join ('1m 1')
By defining a generic T (the name can be customized, usually T) in the function, we can constrain first,second type consistency, and when we try to call the argument type is inconsistent, then the editor will report an error.
Function map (params: t []) {return params} map ([1])
This form is also possible, although the definition T is not displayed when the call is made, but TS can infer the type of T. T [] is a way for an array to define a type, indicating the type of each value in the array.
Note: there will be warnings in the form of Array after 3. 4. Square brackets are used uniformly.
This is a single generic type, but there are often multiple generics in the actual scenario:
Function result (first: TMagna second: U) {return `${first} + ${second}`} join (1meme 1') join / / this form is also available.
Generics are so easy to use that it's certainly impossible to use them only in functions, so let's move on to using generics in the following classes:
Class DataManager {constructor (private data: string [] | number []) {} getItem (index: number): string | number {return this.data [index]}} const data = new DataManager ([1]) data.getItem (0)
The constructor in the DataManager class defines the type of data by combining types, which is obviously not desirable in complex business scenarios, because if we are not sure about the type, before passing parameters, we can only write more types or define them as any types, which is very inflexible. At this time, we think of generics, can they be applied to classes?
The answer is yes.
Class DataManager {constructor (private data:) {} getItem (index: number): {return this.data [index]}} const data = new DataManager ([1]) / / const data = new DataManager ([1]) / / intuitive, and the above equivalent data.getItem (0)
It seems to be very flexible, but there is another problem. If there are no rules, the function writer allows the caller to be flexible in passing parameters, but it needs to conform to some logic within the function, that is, the previous function return this.data [index], but now in the function logic, it returns this.data [index] .name, that is, the function caller can pass T type in. But what if each item must have a name attribute?
Then we can define another interface and let T inherit the interface, which can not only maintain flexibility, but also conform to the functional logic.
Interface Item {name: string} class DataManager {constructor (private data: t []) {} getItem (index: number): number {return this.data [index] .name}} const data = new DataManager ([name: 'dell'])
At this point, generics are almost over, but there is another question. The above number | string union type wants to be constrained by generics. How to write it, that is, T can only be string or number.
Class DataManager {constructor (private data: t []) {} getItem (index: number): t {return this.data [index]}}
Namespace
At this point, we have built a lot of new demo files before, I don't know if you have found such a strange phenomenon.
Demo.ts
Let a = 123 Universe / dosomething
Demo1.ts
Let a = '123'
When we define the variable an in the demo1.ts file, a will be marked red, telling us that a has been declared as number. Why?
We obviously did not define an in the demo1.ts file, and then take a closer look at the prompt, it tells us that it has been defined in demo.ts. Partners who are proficient in JS must know that it should be a matter of modularity.
Yes, TS, like JS, does not have a top-level import or export declaration in a file, and its content is globally visible, in other words, if we have import or export in our file, it is a modularization.
Export const let a = '123'
There will be no problem, so let's take a look at the following code:
Class A {/ / do something} class B {/ / do something} class C {/ / do something} class D {constructor () {new A () new B () new C ()}}
In the code, I defined four classes, mentioned above, if I export the class D through export, so that other files can continue to use An or several other class names, but I now have a requirement that I do not want to expose the three classes A, B, C, and whether I can call the D class through the object outside. When namespace enters the stage, take a look at the code:
Namespace Total {class A {/ / do something} class B {/ / do something} class C {/ / do something} export class D {constructor () {new A () new B () new C ()}} Total.D
It's OK to write in this way. You can only call D through namespace. If you want to call other classes, just go to the export class in front of it.
Namespace in the actual development, we generally use to write some .d.ts files. That is, the JS interpretation file.
A namespace is essentially an object, and its function is to turn a series of related global variables into properties of an object, and take a look at how the above code is compiled into JS.
Var Total; (function (Total) {var A = / * * @ class * / (function () {function A () {} return A;} ()); var B = / * * @ class * / (function () {function B () {} return B;} ()) Var C = / * * @ class * / (function () {function C () {} return C;} ()); var D = / * * @ class * / (function () {function D () {new A (); new B (); new C ();} return D } (); Total.D = D;}) (Total | | (Total = {})); Total.D
As you can see from the above, the function is executed immediately and a variable is passed in, and then the exported method is mounted on the variable so that the class can be called outside in the form of object properties.
Finally, add declare, which is used to write a declaration file for a third-party JS library so that you can get the corresponding code completion and interface hints:
/ / the commonly used declaration type declare function declares the global method declare class declares the global class declare enum declares the global enumeration type declare global extension global variables declare module extension module
You can also use declare as a module supplement. The following is an official example:
/ / observable.ts export class Observable {/ /... Implementation left as an exercise for the reader.} / / map.tsimport {Observable} from ". / observable"; declare module ". / observable" {interface Observable {map (f: (x: t) = > U): Observable;} Observable.prototype.map = function (f) {/ /. Another exercise for the reader} / / consumer.tsimport {Observable} from ". / observable"; import ". / map"; let o: Observable; o.map (x = > x.toFixed ())
The code means to customize a file in map.js, add the map method of the type you want and implement the function to be mounted on the Observable prototype, and then you can use the map in the Observable type in consumer.ts.
TypeScript Advanced Syntax
Class decorator
Decorator we have been in contact with JS for a long time, and in another Chat article entitled "Writing Design patterns for Front-end students that are easy to understand and grasp", I also explained the decorator pattern in detail. Students who are interested in design patterns are welcome to subscribe. The decorator is essentially a function. @ description is actually a grammatical candy. TS and JS decorators use more or less the same, so let's look at a simple example:
Function Decorator (constructor: any) {console.log ('decorator')} @ Decorator class Demo {} const text = new Test ()
When we feel perfect, the editor gives us a red mark:
In fact, the decorator is an experimental syntax, so it cannot be used directly. You need to open the experimental support and modify the following two options of tsconfig:
"experimentalDecorators": true, "emitDecoratorMetadata": true
After modifying the configuration, it is found that the terminal outputs correctly.
But here I would like to ask one more question, when is the time for the decorator to run, and when the class is instantiated?
In fact, the decorator has already run the decorator when the class is created, and can comment out the instantiated statement by itself, and then run it to see if the console has log.
The class's decorator function takes an argument to the class's constructor, so we can change the Decorator to verify it:
Function Decorator (constructor: any) {constructor.prototype.getResult = () = > {console.log ('constructor')}} @ Decorator class Demo {} const text = new Test () text.getResult ()
The console prints out the constructor correctly to prove that the received parameter is indeed the constructor of the class. In the above code, we only use one decorator in the class, but you can actually use multiple decorators for a class, as follows:
@ Decorator @ Decorator1 class Demo {}
The execution order of multiple decorators is first down and then up.
In the above decorator writing, we decorate the whole function to the class, but the reality is that my function has some logic and is not used for class decoration, so we write a factory pattern to decorate the class:
Function Decorator () {/ / do something return function (constructor: any) {console.log ('descorator')}} @ Decorator () class Test ()
In this way, we can pass some parameters in, and then the function inside to control the decoration of the decorator.
I don't know if you have found that when we are validating the decorator parameters, when we call our method mounted on the decorator prototype through an instance of the class, although there is no error, the editor does not give us a hint. This is not in line with our expectations. The above decorator is easy to write, but intuitive.
But in TS we often use it in a way like this, and we can also solve the problem mentioned above:
Function Decorator () {return function any > (constructor: t) {return class extends constructor {name = 'bbb' getName}} const Test = Decorator () (class {name: string constructor (name: string) {console.log (this.name,'1') this.name = name console.log (this.name) '2')}) const test = new Test (' aaa') console.log (test.getName ())
We greatly changed the previous code, it seems to be a lot more sophisticated, but it is also very difficult to understand. Don't worry, let me explain one by one.
Any >
This is a generic type, T inherits a constructor or a class, and the constructor parameter is an expansion operator that receives multiple arguments.
In this way, generic T can be used to define constructor. And the Decorator function, as above, we write it as a function Corey and pass the class as an argument, abandoning the previous syntax sugar, so that the editor can give us a hint when we call a method decorated on the class.
Method decorator
In the previous section, after sharing the class decorator, we are sure that we are still interested in the decorator. In this section, we will share the method decoration for the class. Let's take a look at the last code:
Function getNameDecorator (target: any, key: string, descriptor: PropertyDescriptor) {console.log (target);} class Test {name: string constructor (name: string) {this.name = name} @ getNameDecorator getName () {return this.name}} const test-new Test ('aaa') console.log (test.getName ())
This implements decorating the method of the class. When we decorate the ordinary method of the class, the parameter target received in the decorator function corresponds to the name of the common method whose prototype,key is decorated.
Note that what I said above is the common method. Like the class decorator, the method decorator is executed when the method is defined.
I've just emphasized the general method, and then I'm going to talk about the static method.
Class Test {name: string constructor (name: string) {this.name = name} @ getNameDecorator static getName () {return this.name}}
In the decorator function of the static method, the first parameter target corresponds to the constructor of the class.
Class's method decorator function, we still have one parameter that we haven't mentioned, and that is descriptor.
I don't know if you have noticed that this function takes three arguments, and the third argument is still descriptor, which is a bit like Object.defineProperty this API. When we call descriptor in the function, the editor will give us a hint.
These properties are the same as the descriptor settable properties in Object.defineProperty, and yes, the functions are the same. For example, if we don't want the getName method to be rewritten externally, then we can do this:
Function getNameDecorator (target: any, key: string, descriptor: PropertyDescriptor) {console.log (target); descriptor.writable = false}
When you try to modify it in this way, running the compiled file will report an error:
Const test = new Test ('aaa') console.log (test.getName ()) test.getName = () = > {return' aaa'}
This is the result of the run:
Accessor decorator
Add accessors to ES6's class and access properties through get and set methods. If you digest all the knowledge above, the use of the accessor decorator is the same.
Function visitDecorator (target: any, key: string, descriptor: PropertyDescriptor) {} class Test {provate _ name: string constructor (name: string) {this._name = name} get name () {return this._name} @ visitDecorator set name () {this._name = name}}
The usage of the accessor decorator is similar to that of the common method decorator of a class, so I won't expand it here. Similarly, in the class, we can also add decorators to properties and parameters.
Decorator business scenarios use
Before we spent a lot of time to introduce decorators, this section will share with you the use of decorators in actual business scenarios. First, let's look at a piece of code like this:
Const uerInfo: any = undefined class Test {getName () {return userInfo.name} getAge () {return userInfo.name}} const test = new Test () test.getName ()
This code does not need to be run, we can know that it will report an error, because userInfo does not have a name attribute. So if we want not to report an error, it will be written like this:
Class Test {getName () {try {return userInfo.name} catch (e) {console.log ('userInfo.name does not exist')} getAge () {try {return userInfo.age} catch (e) {console.log ('userInfo.age does not exist')}
There seems to be no problem if you change the class to this way. Why do you say it seems?
That's because there's nothing wrong with running, but if we have a lot of methods like this, do we have to handle errors over and over again? Can you use the decorator mentioned earlier to handle errors:
Const userInfo: any = undefined function catchError (target: any, key: string Descriptor: PropertyDescriptor) {const fn = descriptor.value descriptor.value = function () {try {fn ()} catch (e) {console.log ('userinfo has a problem')}} class Test {@ catchError getName () {return userInfo.name} @ catchError getAge () {return userInfo.age}}
In this way, we extract the logic of catching exceptions and reuse them through decorators.
However, it is a little different from what we wrote before, that is, the error message is the same, and we do not know which function reported the error. That is to say, we hope that the decorator function can receive a parameter to improve the error message. In this way, we can use the decorator as a factory function, as mentioned above. The code is as follows:
Function catchError (msg: string) {return function (target: any, key: string) Descriptor: PropertyDescriptor) {const fn = descriptor.value descriptor.value = function () {try {fn ()} catch (e) {console.log (`userinfo.$ {msg} problems `)} class Test {@ catchError ('name') ) getName () {return userInfo.name} @ catchError ('age)' getAge () {return userInfo.age}}
In this way, our code can meet our needs, and later we will add other functions, which can also be decorated with decorators.
Application of TypeScript in the project
Scaffolding to build a TypeScript
Now the development is becoming more and more professional, usually we initialize a project, if we do not use scaffolding for development, we need to configure a lot of things, such as package.json, .gitignore, and some build tools, such as webpack and their configuration.
When we go to write a project in TypeScript, we also need to configure the compiled configuration file tsconfig and tslint.json file of TypeScript.
If we just want to do a small project or just want to learn this piece of development, then the early knife sharpening preparation work will make many people daunted and confused. Therefore, a scaffolding tool can help us sharpen the knife, and polished brightly, this tool is TypeScript Library Starter. Let's find out together.
Looking at its website, we know that this is an open source scaffolding tool based on TypeScript, which helps us start a TypeScript project quickly, using the following methods:
Git clone https://github.com/alexjoverm/typescript-library-starter.git ts-project cd ts-projectnpm install
These lines of commands mean to pull down the code and rename the project. Go to the project, install the dependencies for the project through npm install, and then take a look at our file directory:
├── package.json / / Project profile
├── rollup.config.ts / / rollup configuration file ├── src / / Source directory ├── test / / Test directory ├── tools / / some configuration script tools published to GitHup pages and published to npm ├── tsconfig.json / / TypeScript compile configuration file └── tslint.json / / TypeScript lint file
The project created by TypeScript library starter does integrate many excellent open source tools, including packaging, unit testing, formatting code, etc., which can be studied deeply by students who are interested.
It is also important to note that TypeScript library starter helps us configure a complete set of front-end workflows in package.json:
Npm run lint: use the TSLint tool to check the readability, maintainability, and functional errors of the TypeScript code in the src and test directories.
Npm start: observer mode runs the rollup tool packaging code.
Npm test: run the Jest tool to run the unit test.
Npm run commit: run the commitizen tool to submit formatted git commit comments.
Npm run build: run rollup to compile and package the TypeScript code, and run the typedoc tool to generate documentation.
Some other commands are not used very much in our daily development, and students in need can understand them by themselves.
TypeScript actual combat
Now that most of our front-end projects are developed using frameworks, today I will introduce how to use React + TypeScript for React project development. Of course, here we will use the scaffolding provided by React to quickly build the project framework. In order to prevent your local version of scaffolding from affecting the development of TypeScript, it is recommended to implement:
Npm uninstall create-react-app
Then execute the official React TypeScript generation command:
Npx create-react-app react-project-template typescript-use-npm
This command means to download the latest scaffolding (if the current environment does not have this scaffolding), and then use create-react-app scaffolding to generate typescript as the development template of the project, the project name is react-project, and through npm to install dependencies, if there is no-- use-npm will default to use Yarn.
After the project is built, we sort out the files, delete some files we don't use, and delete the relevant references as well. The final file directory is as follows:
When we use TS to write React, jsx becomes tsx. In the APP.tsx file:
Const App: React.FC = () = > {return}
A function type of React.FC is defined through React.FC, which is the function type defined in React.
Front-end UI development, now there are many packaged frameworks on the market, so that we can quickly build a page, here we choose ant-design, this framework is also developed using TypeScript, so when we use it for development, there will be many types available for us to use, so using it to consolidate the TypeScript knowledge we just learned will have more benefits.
First, let's install this component library:
Npm install antd-save
After installation, introduce the CSS style into index.tsx:
Import 'antd/dist/antd.css'
Next, let's write a login page and create a new login.css on the home page:
.login-page {width: 300px; padding: 20px; margin: 100px auto; border: 1px solid # ccc;}
Then we go to the antd-design official website and copy the login component code to our App.ts:
Import React from "react"; / / import ReactDOM from 'react-dom' import ". / login.css"; / / function App () {/ / return Hello world; / /} / / export default App; import {Form, Input, Button, Checkbox} from "antd"; / / import {Store} from "antd/lib/form/interface"; import {ValidateErrorEntity, Store} from "rc-field-form/lib/interface" Const layout = {labelCol: {span: 8,}, wrapperCol: {span: 16,}}; const tailLayout = {wrapperCol: {offset: 8, span: 16,},}; const App = () = > {const onFinish = (values: Store) = > {console.log ("Success:", values);} / / const onFinishFailed = (errorInfo: Store) = > {const onFinishFailed = (errorInfo: ValidateErrorEntity) = > {console.log ("Failed:", errorInfo);}; return (Remember me Submit) }; / / ReactDOM.render (, mountNode); export default App
Among them, the values editor of the onFinish function gives us a hint of hidden trouble, and we can't determine the type of value, but we can't fill in any. Therefore, we can look for the types defined in Form. Mac users place their mouse over the From tag in import (windows users press and hold cmd), enter the source code, and then go all the way to find the definition of our method. First, we enter:
Then InternalForm inherited InternalForm, and we continued to look for it, and finally found the source:
In the same way, we can find onFinishFailed:
Finally, you can introduce these two types into the file.
After the above tests, even if our project is basically set up, we can continue to enrich the relevant pages.
Here, sort out the files, delete the unnecessary ones, create a new pages directory under the src directory, and then put all our page components here, and store the login code in this folder, and then modify the App.ts:
Import {Route, HashRouter, Switch} from "react-router-dom"; import React from "react"; import LoginPage from ". / pages/login"; import Home from ". / pages/home"; function App () {return ();} export default App
Because react-router-dom is a file written by JS, you need to install another type definition file:
Npm install @ types/react-router-dom-D at this point, the study of "what are the knowledge points about TypeScript" is over. I hope I can solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.