Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand Typescript code

2025-03-29 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces "how to understand Typescript code". In daily operation, I believe many people have doubts about how to understand Typescript code. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful for you to answer the doubts about "how to understand Typescript code"! Next, please follow the editor to study!

I. basic norms

(1) constant

Constants must be named, and when making logical judgments, it is not allowed to directly compare unnamed constants.

Erroneous writing

Switch (num) {case 1:... Case 3:... Case 7:...} if (x = 0) {...}

In the above example, there is no idea what 137 corresponds to, and this writing is basically unreadable.

The correct way to write

Enum DayEnum {oneDay = 1, threeDay = 3, oneWeek = 7,} let num = 1; switch (num) {case DayEnum.oneDay:. Case DayEnum.threeDay:... Case DayEnum.oneWeek:...} const RightCode = 0; if (x = RightCode)

From the above correct writing, we can see that the constant has a name. In the logical judgment such as switch or if, we can know the specific meaning of the constant from the variable name, which increases the readability.

(2) enumeration

In addition to constant enumerations, during the compilation phase of Typescript, enumerations generate a maping object, or even a bi-directional mapping if not string enumerations. So in our business code, with enumerations, we don't need an array associated with enumerated values.

The wrong way of writing

Enum FruitEnum {tomato = 1, banana = 2, apple = 3} const FruitList = [{key:1, value: 'tomato'}, {key:2, value:' banana'}, {key:3, value: 'apple'}]

The reason for the error here is redundancy. If we want to get a FruitList, we do not need a new, but we can generate an array directly according to the enumeration of FruitEnum. The principle is that the enumeration of Typescript, in addition to constant enumeration, will generate a map object when compiling.

The correct way to write

Enum FruitEnum {tomato = 1, banana = 2, apple = 3} const FruitList = Object.entries (FruitEnum)

The above is the correct way to write, this kind of writing is not only non-redundant, in addition, if you modify the type of enumeration, we just need to modify the enumeration directly, so that the derived array will also change.

In addition, there is an essential difference between string enumeration values and strings, so be careful when defining types, otherwise it will make the code you write redundant.

The wrong usage

Enum GenderEnum {'male' =' boy', 'female' =' girl'} interface IPerson {name:string gender:string} let bob:IPerson = {name: "bob", gender:'male'} {Gender [bob.gender as keyof typeof GenderEnum]}

The reason for the above error is that in the type definition of IPerson, gender should not be string, but an enumerated key, so when you convert string to enumerated values, you must add an as keyof typeof GenderEnum assertion.

The correct way to write

Enum GenderEnum {'male' =' boy', 'female' =' girl'} interface IPerson {name:string gender:keyof typeof GenderEnum} let bob:IPerson = {name: "bob", gender:'male'} {Gender [bob.gender]}

The above is the correct way to write, there is a clear difference between string enumeration and string type, when a variable needs to use enumeration, it can not be defined as string.

(3) ts-ignore & any

The fact that ts-ignore,ts-ignore should be strictly prohibited in Typescript is a factor that affects the quality of Typescript code even more than any. For any, I once wanted to disable any in my project, but there are scenarios where any is required, so there is no rude prohibition on the use of any. But in most scenarios, you may not need to use any. Scenarios that need to use any can be analyzed by case by case.

Scenarios where ts-ignore is used incorrectly

/ / @ ts-ignore import Plugin from 'someModule' / / if Plugin.test ("hello world") does not exist in someModule's declaration

The above is the most classic scenario of using ts-ignore, using ts-ignore. Then Typescript will think that the type of Plugin is any. The correct method customizes the types that need to be used through the declare module method.

The right way.

Import Plugin from 'someModule' declare module' someModule' {export type test = (arg: string) = > void;}

Declarations can be defined within module, and declarations of the same name follow certain merging principles, and declare module is convenient if you want to extend the tripartite module.

In most of the same scenarios, you don't need to use any. In some scenarios, if you can't immediately determine the type of a value, we can use unknown instead of using any.

Any will completely lose type judgment, which is actually quite dangerous, and using any is tantamount to giving up type checking, which basically gives up typescript. For example:

Let fish:any = {type:'animal', swim: () = > {}} fish.run ()

In the above example, we call a method that does not exist, because we use any, so we skip static type checking, so it is not safe. Errors occur at run time, and if you can't immediately determine the type of a value, we can use unknown instead of using any.

Let fish:unknown = {type:'animal', swim: () = > {}} fish.run () / will report an error

Unkonwn is a subtype of any type, so like any, any type can be assigned to unkonwn. Unlike any, unkonwn variables must specify their own type, type contraction, or type assertion before unkonwn variables can properly use the methods and variables defined on them.

To put it simply, unkonwn needs to force its type before using it.

(4) namespace

In Typescript code, especially in the development of partial business, you basically don't use namespace. In addition, module is naturally supported in nodejs, and es module has become a language-level specification in es6 (next), so Typescript officially recommends the use of module.

Namespace is simply a global object, of course, we can also put namespace in module, but namespace in module is also problematic.

The wrong way.

/ / use export namespace Shapes {export class Triangle {/ *... * /} export class Square {/ *... * /}} / / when we use shapes.ts / / shapeConsumer.ts import * as shapes from ". / shapes" in a shapes.ts module; let t = new shapes.Shapes.Triangle (); / / shapes.Shapes?

The right method (using module directly)

Export class Triangle {/ *... * /} export class Square {/ *... * /}

The above direct use of module is the right approach, in the module system itself can avoid duplicate variable naming, so namespace is meaningless.

(5) limit the number of function parameters.

When defining a function, the number of function parameters should be reduced, and no more than 3 are recommended.

The wrong usage

Function getList (searchName:string,pageNum:number,pageSize:number,key1:string,key2:string) {...}

It is not recommended that the function has more than 3 arguments, and when there are more than 3, you should use objects to aggregate.

The correct usage

Interface ISearchParams {searchName:string; pageNum:number; pageSize:number; key1:string; key2:string;} function getList (params:ISearchParams) {}

The same applies to the React project, and the same goes for useState

Const [searchKey,setSearchKey] = useState (''); const [current,setCurrent] = useState (1) const [pageSize,setPageSize] = useState (10) / / incorrect const [searchParams,setSearchParams] = useState ({searchKey:', current:1, pageSize:10}) / / correct

(6) the module module ensures no side effects as far as possible.

Please do not use the side effects of the module. To ensure that the use of the module should be import before use.

The wrong way.

/ / Test.ts window.x = 1; class Test {} let test = new Test () / / index.ts import from'. / test'.

The above import module in index.ts is called inside the test.ts file. This method is to import a module with side effects.

The correct way should be to ensure the purity of the module's non-export variables, and the caller should import before calling when using the module.

The right way.

/ / test.ts class Test {constructor () {window.x = 1}} export default Test / / index.ts import Test from'. / test' const t = new Test ()

(7) use is prohibited!. Non-null assertion

Non-null assertions are not safe in themselves, and there are errors in subjective judgment. From the point of view of defensive programming, it is not recommended to use non-empty assertions.

The wrong usage

Let x:string | undefinedundefined = undefined xroom.toString ()

Because non-null assertions are used, there is no error at compile time, but an error at run time.

Optional chaining is recommended. To?. In the form of.

(8) use the built-in functions of typescript

Many of typescript's built-in functions can reuse some definitions. Not one by one here, the common ones are Partial, Pick, Omit, Record, extends, infer, and so on. If you need to derive new types from existing types, then using built-in functions is simple and convenient. You can also use union types, crossover types, and type merging.

Joint type

/ / basic type let x:number | string x = 1; x = "1" / / Multi-literal type let type:'primary' | 'danger' |' warning' | 'error' =' primary'

What is worth noting is the literal quantity assignment.

Let type:'primary' | 'danger' |' warning' | 'error' =' primary' let test = 'error' type = test / / error let test =' error' as const type = test / / correct

Cross type

Interface ISpider {type:string swim: () = > void} interface IMan {name:string; age:number;} type ISpiderISpiderMan = ISpider & IMan let bob:ISpiderMan = {type: "11", swim: () = > {}, name: "123", age:10}

Type merging

Finally, let's talk about type merging, which is a highly deprecated method. In business code, type merging is not recommended, which increases the reading complexity of the code. There are many places for type merging. Type merging can be performed among class, interface, namespace, and so on. Take interface as an example:

Interface Box {height: number; width: number;} interface Box {scale: number;} let box: Box = {height: 5, width: 6, scale: 10}

Type merging occurs in the above interface Box of the same name. Type merging is not only possible for interface and interface, but also for class, interface,class, namesppace, and so on. Type merging is not recommended in business code.

(9) encapsulate conditional statements and type guards of ts

The wrong way of writing

If (fsm.state = = 'fetching' & & isEmpty (listNode)) {/ /...}

The correct way to write

Function shouldShowSpinner (fsm, listNode) {return fsm.state = = 'fetching' & & isEmpty (listNode);} if (shouldShowSpinner (fsmInstance, listNodeInstance)) {/ /.}

In the correct writing, we encapsulate the logic of conditional judgment into an independent function. This way of writing is more readable, and we can know what judgment has been made from the name of the function.

In addition, encapsulation conditional statements can also be linked to ts's custom type guards. Take a look at one of the simplest custom type guards that encapsulate conditional statements.

Function IsString (input: any): input is string {return typeof input = 'string';} function foo (input: string | number) {if (IsString (input)) {input.toString () / / judged to be string} else {}

The rational use of custom guards in a project can help us reduce many unnecessary type assertions and improve the readability of the code.

(10) do not use nonvariables

Whether it is a variable name or a function name, please do not use non-naming. I have encountered this problem in my business. The backend defines an unnamed variable isNotRefresh:

Let isNotRefresh = false / / whether not refresh, no means refresh

IsNotRefresh means no refresh, and a variable defined in this way will cause a lot of logic associated with this variable to be reversed. The correct form would be to define whether the variable isRefresh indicates whether to refresh or not.

Let isRefresh = false / / whether to refresh, which means refresh

Second, functional formula

Personally, functional programming is highly recommended. Subjectively, chained calls are better than callbacks, and functional calls are better than chained calls. In recent years, functional programming has become increasingly popular. Functional features are used in many open source libraries, such as Ramdajs, RxJS, cycleJS, lodashJS and so on. This article focuses on how to use ramdajs to simplify code.

(1) declarative and imperative

Personally, I think function declarative calls are more concise than imperative calls, for example:

/ / imperative let names:string [] = [] for (let item.name)

From the above example, we can see that the declarative method of function call is obviously more concise. In addition, for functions with no side effects, such as the map function above, you can focus on writing business code regardless of how it is implemented inside the function. When optimizing your code, you only need to focus on the interior of these stable and robust functions.

(2) Ramdajs

It is recommended that ramdajs,ramdajs is an excellent functional programming library. Compared with other functional programming libraries, ramdajs is automatically corrified, and the functions provided by ramdajs never change the user's existing data.

A simple example from the recent business code:

/ * get tag list * / const getList = async () = > {pipeWithP ([() = > setLoading (true), async () = > request.get ('', {params: {action: API.getList},})) Async (res: IServerRes) = > {R.ifElse (R.isEqual (res.message = 'success'), () = > setList (res.response.list)) ) ();}, () = > setLoading (false)] ();}

The above is an example of business code, using pipe to make the operation of the process clearer, and there is no need to define intermediate variables.

Let's look at another example:

Let persons = [{username: 'bob', age: 30, tags: [' work', 'boring']}, {username:' jim', age: 25, tags: ['home',' fun']}, {username: 'jane', age: 30, tags: [' vacation', 'fun']}]

We need to find the objects in which tags contains fun from this array. If you use imperative:

Let NAME = 'fun' let person; for (let iCombitor I R.map (ctem = > R.map (dtem = > R.map (etem = > etem.name.toUpperCase () (dtem)) (ctem)) (btem)) (atem),) (arr); at this point, the study on "how to understand Typescript code" is over, hoping to solve everyone's 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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report