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 TypeScript recommendations for capturing more Bug". In daily operation, I believe many people have doubts about capturing more Bug TypeScript suggestions. Xiaobian 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 of "what are the TypeScript suggestions for capturing more Bug?" Next, please follow the editor to study!
1. Thoughts on runtime check provided by TypeScript
A common misconception about TypeScript is that as long as a variable is marked with a type, it always checks to see if its data type is consistent with our expectations.
The idea that echoes this misunderstanding is that type tagging of an object returned from the back end can be checked while the code is running to ensure that the object type is correct.
However, this idea is wrong! Because TypeScript is eventually compiled into JavaScript code, and JavaScript is also running in the browser. At this point, all type information is lost, so TypeScript cannot automatically validate the type.
A good way to understand this is to look at the compiled code:
Interface Person {name: string; age: number;} function fetchFromBackend (): Promise {return fetch ('http://example.com'). Then ((res) = > res.json ())} / / compiled function fetchFromBackend () {return fetch (' http://example.com'). Then (function (res) {return res.json ();})}
You can see that the interface definition has completely disappeared after compilation, and there won't be any verifying code here.
But you'd better do run-time checking yourself, and there are many libraries that can help you do that. Keep in mind, however, that this is bound to incur performance overhead.
* consider performing run-time checks on all externally provided objects (such as objects obtained from the back end, JSON deserialized objects, etc.)
two。 Do not define a type as any
When using TypeScript, you can declare the type of a variable or function parameter as any, but doing so also means that the variable is out of type safety.
However, declaring an any type can also be beneficial, and it can be helpful in some scenarios (such as gradually adding types to an existing JavaScript code base, usually when upgrading the code base from js to ts). But it is also like an escape hatch, which greatly reduces the type safety of the code.
Type safety is most effective when it covers as much code as possible. Otherwise, there will be vulnerabilities in the security network, and vulnerabilities may spread through vulnerabilities. For example, if the function returns any, all expression types that use its return value will also become any.
So you should try to avoid using the any type. Fortunately, TypeScript3.0 introduces a type-safe alternative, unknown. You can assign any value to a variable of type unknown, but you cannot assign a value of a variable of type unknown to any variable (unlike any).
If your function returns a value of type unknown, the caller needs to perform a check (using type protection), or at least explicitly convert the value to a specific type. (translator's note: if you don't understand this paragraph, you can refer to this article, the example section of the unknown type)
Let foo: any; / / anything can be assigned to foo foo = 'abc'; / / foo can be assigned to anything const x: number = foo; let bar: unknown; / / anything can be assigned to bar bar =' abc'; / / COMPILE ERROR! Type 'unknown' is not assignable to type' number'. Const y: number = bar
Using the unknown type can be troublesome at times, but it also makes the code easier to understand and makes you pay more attention to it when developing.
In addition, you need to turn on noImplicitAny and throw an error whenever the compiler infers that the type of a value is any. In other words, it allows you to explicitly mark all the scenarios where any will appear.
Although the ultimate goal is to eliminate the presence of any, it is still useful to state explicitly that any is still useful: for example, it is easier to capture them in code review.
* do not use the any type and enable noImplicitAny
3. Turn on strictNullChecks
How many times have you seen this kind of error message?
TypeError: undefined is not an object
I bet many times that one of the most common sources of bug in JavaScript (and even software programming) is forgetting to handle null values.
Use null or undefined to represent null values in JavaScript. Developers are often optimistic that a given variable will not be empty, so they forget to deal with null values.
Function printName (person: Person) {console.log (person.name.toUpperCase ());} / / RUNTIME ERROR! TypeError: undefined is not an object / / (evaluating 'person.name') printName (undefined)
By turning on strictNullChecks, the compiler forces you to do relevant checks, which plays an important role in preventing this common problem.
By default, each type of typescript contains the values null and undefined. That is, null and undefined can be assigned to any variable of any type.
Turning on strictNullChecks changes the behavior. Since undefined cannot be passed as a parameter of type Person, the following code will make an error in the compilation time.
/ / COMPILE ERROR! / / Argument of type 'undefined' is not assignable to parameter of type' Person'. PrintName (undefined)
What if you really want to pass undefined to printName? Then you can adjust the type signature, but you will still be asked to deal with the undefined case.
Function printName (person: Person | undefined) {/ / COMPILE ERROR! / / Object is possibly 'undefined'. Console.log (person.name.toUpperCase ());}
You can fix this error by making sure that person is defined:
Function printName (person: Person | undefined) {if (person) {console.log (person.name.toUpperCase ());}}
Unfortunately, strictNullChecks is not enabled by default, and we need to configure it in tsconfig.json.
In addition, strictNullChecks is part of a more generic strict mode, which can be enabled through the strict flag. You should definitely do this! Because the stricter the compiler settings, the more bug you can find as soon as possible.
* always turn on strictNullChecks
4. Turn on strictPropertyInitialization
StrictPropertyInitialization is another flag that belongs to the strict pattern flag set. It's important to turn on strictPropertyInitialization, especially when using Class, which is actually a bit like an extension to strictNullChecks.
If strictPropertyInitialization is not enabled, TS allows the following code:
Class Person {name: string; sayHello () {/ / RUNTIME ERROR! Console.log (`Hello from ${this.name.toUpperCase ()} `);}}
There is an obvious problem here: this.name is not initialized, so calling sayHello at run time will report an error.
The root cause of this error is that the property is not assigned in the constructor or using a property initializer, so it (at least initially) is undefined, so its type becomes string | undefined.
Turning on strictPropertyInitialization prompts the following error:
Property 'name' has no initializer and is not assigned in the constructor.
Of course, if you assign values in the constructor or using property initializers, the error will disappear.
* always turn on strictPropertyInitialization
5. Remember to specify the return type of the function
TypeScript makes you highly dependent on type inference, which means that as long as TS can infer types, you don't need to label types.
However, this is like a double-edged sword. On the one hand, it is very convenient and reduces the trouble of using TypeScript. On the other hand, sometimes the inferred type may be inconsistent with your expectations, thus reducing the protection provided by using static types.
In the following example, instead of indicating the return type, we let TypeScript infer the return value of the function.
Interface Person {name: string; age: number;} function getName (person: Person | undefined) {if (person & & person.name) {return person.name;} else if (! person) {return "no name";}}
At first glance, we may think our method is safe and always return the string type, but when we explicitly declare the (expected) return type of the function, we will find that an error has been reported.
/ / COMPILE ERROR! / / Function lacks ending return statement and return type does not include 'undefined'. Function getName (person: Person | undefined): string {/ /...}
By the way, this error will not be detected until you turn on strictNullChecks.
The above error indicates that the return value of the getName function does not cover a situation where person is not empty but person.name is empty. In this case, all if conditions are not equal to true, so undefined is returned.
Therefore, TypeScript infers that the return type of this function is string | underfined, while we declare string. (translator's note: so proactively declaring the return value type of a function helps us to catch some imperceptible bug in advance.)
* always label the return value type of the function
6. Do not store implicit type variables in objects
Type checking for TypeScript can be subtle.
In general, TypeScript allows objects of type A to be assigned to variables of type B when type A has at least the same properties as type B. This means that it can contain other properties.
/ / for example: type A = {name: string; age: number;}; type B = {name: string;}; let a: a = {name: 'John', age: 12,}; let b: B; / / compile success b = a
However, if you pass the literal amount of the object directly, the behavior is different. TypeScript allows the target type (pass) only if it contains the same attribute. Other properties are not allowed at this time.
Interface Person {name: string;} function getName (person: Person): string | undefined {/ /...} / / ok getName ({name: 'John'}); / / COMPILE ERROR / / Argument of type' {name: string; age: number;}'is not assignable to parameter of type 'Person'. GetName ({name: 'John', age: 30})
If we don't pass the literal amount of the object directly, but instead store the object in the constant (and then pass it), it doesn't seem to make any difference. However, this changes the behavior of type checking:
Const person = {name: 'John', age: 30}; / / OK getName (person)
Passing extra attributes may cause bug (for example, when you want to merge two objects). Understand this behavior and, where possible, pass the literal amount of the object directly.
* pay attention to how to pass the object to the function and always consider whether it is safe to pass additional properties
7. Don't overuse type assertions
Although TypeScript can make a lot of inferences about your code, sometimes you know the details of a value better than TypeScript. At this point, you can tell the compiler through type assertions, "believe me, I know what I'm doing."
For example, assert an object that is requested from the server, or assert an object of a subtype as a parent type.
Type assertions need to be used conservatively. For example, it should never be used when the type of parameter passed by the function does not match.
There is a safer way to use type assertions: type protection. Type protection is a function that asserts its parameter type when true is returned. It provides run-time detection of the code, giving us more confidence in whether the incoming variables meet expectations.
In the following code, we need to use type assertions because TypeScript does not know the type of object returned from the back end.
Interface Person {name: string; age: number;} declare const fetchFromBackend: (url: string) = > Promise; declare const displayPerson: (person: Person) = > void; fetchFromBackend ('/ person/1'). Then ((person) = > displayPerson (person as Person))
We can improve the code by using type protection and providing a simple run-time check. Let's assume that as long as an object has name and age properties, its type is Person.
Const isPerson = (obj: Object): obj isPerson = > 'name' in obj & &' age' in obj; fetchFromBackend ('/ person/1'). Then ((person) = > {if (isPerson (person)) {/ / Type of `person`person`Person` here! DisplayPerson (person);}})
You can find that, thanks to type protection, the type of person can be inferred correctly in the if statement.
* consider using type protection instead of type assertions
8. Do not use extension operators for Partial types
Partial is a very useful type that makes every attribute of the source type optional.
Partial has a good practical usage scenario: when you have an object type that represents a configuration or option, and you want to create a subset of the configuration object to override it.
You might write code like this:
Interface Settings {a: string; b: number;} const defaultSettings: Settings = {/ * /}; function getSettings (overrides: Partial): Settings {return {... defaultSettings,... overrides};}
This looks good, but it actually reveals a vulnerability in TypeScript's type system.
Looking at the code below, the type of result is Settings, while the value of result.an is undefined.
Const result = getSettings ({a: undefined, b: 2})
Because extending Partial is a common pattern, and one of the goals of TypeScript is to strike a balance between rigor and convenience, it can be said that the design of TypeScript itself brings this inconsistency. However, it is still very important to be aware of the problem.
* do not use extension operators on Parital objects unless you are sure that the object does not contain an explicit undefined
9. Don't put too much faith in Record types
This is another example of a subtle situation in TypeScript's built-in type definition.
Record defines an object type where all key have the same type and all value have the same type. This is ideal for mappings and dictionaries that represent values.
In other words, Record is equivalent to {[key: KeyType]: ValueType}.
As you can see from the code below, the type of value returned by accessing the properties of the record object should be the same as that of ValueType. However, you will find that this is not entirely true, because the value of abc will be undefined.
Const languages: Record = {'clockwise:' static', 'java':' static', 'python':' dynamic',}; const abc: string = languages ['abc']; / / undefined
This is another example of TypeScript choosing convenience over rigor. Although it is possible to use this in most cases, you should still be careful.
The easiest way to fix it is to make the second parameter of Record optional:
Const languages: Partial = {'clockwise:' static', 'java':' static', 'python':' dynamic',}; const abc = languages ['abc']; / / abc is infer to string | underfined
* unless you are sure that there is no problem, you can always keep the value type parameter of Record (the second parameter) optional
10. Do not allow substandard type declarations to occur
When defining the type of a business domain object, you usually encounter a situation similar to the following:
Interface Customer {acquisitionDate: Date; type: CustomerType; firstName?: string; lastName?: string; socialSecurityNumber?: string; companyName?: string; companyTaxId?: number;}
This object contains many optional objects. Some of these objects are meaningful and required when Customer represents a person (type = CustomerType.Individual), while others are required when Custormer represents a company (type = CustomerType.Institution).
The problem is that the Customer type does not reflect this! In other words, it allows some illegal combinations of attributes (for example, lastName and companyName are not defined)
This is indeed a problem. You can either perform additional checks or use type assertions to deselect certain fields based on the value of the type attribute.
Fortunately, there is a better solution-discrimination of federation types. Discrimination of federation types adds a function to federation types: different scenarios can be distinguished at run time.
We rewrite the Customer type as two types: a union of Individual and Institution, each containing specific fields and having a common field: type, whose value is a string. This field allows run-time checking, and TypeScript knows it can be handled specifically.
Interface Individual {kind: 'individual'; firstName: string; lastName: string; socialSecurityNumber: number;} interface Institution {kind:' institutional'; companyName: string; companyTaxId: number;} type Customer = Individual | Institution
What's really cool about distinguishing federated types is that TypeScript provides built-in type protection that allows you to avoid type assertions.
Function getCustomerName (customer: Customer) {if (customer.kind = 'individual') {/ / The type of `customer`id `Individual` return customer.lastName;} else {/ / The type of `customer`id `Institution` return customer.companyName;}}
* when encountered with complex business objects, try to consider the use of discriminating federation types. This can help you create types that are more realistic.
At this point, the study of "what are the TypeScript recommendations for capturing more Bug" is over. I hope to be able to 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.