In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-20 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 the generics of C++ TpeScript series". In daily operation, I believe many people have doubts about how to understand the generics of C++ TpeScript series. 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 of "how to understand the generics of C++ TpeScript series". Next, please follow the editor to study!
Catalogue
I. template
2. Generics
Third, generic recursion
Default generic parameters
5. Generic overloading
I. template
Speaking of generics, I have to mention the ancestor of generics, templates. The templates in C++ are famous for their brain-burning and powerful, and have been talked about by all kinds of Daniel for many years. For now, generics in Java, .NET, or TS can all be thought of as a subset of implementing C++ templates. I don't agree with the subset. Because as far as the purpose of existence is concerned, TS and C++ templates are completely different.
C++ templates appear to produce type-safe generic containers. Let's talk about the general container first. For example, I wrote a linked list or an array. This data structure doesn't care much about the specific type of data that exists in it. It can implement the corresponding operation. But js itself doesn't care about type and size, so arrays in js are generic containers. For TS, the emergence of generics can solve this problem. Another thing worth comparing is that C++ templates end up producing the corresponding class or function, but for TS, TS can't produce anything. Some students may have to ask, didn't TS finally produce JS code? This is a bit lax, because TS eventually separates the JS code without doing anything to the original logic.
Another purpose of C++ templates is metaprogramming. This metaprogramming is quite powerful and optimizes program execution mainly through compile-time programming constructs. As far as TS is concerned, only one similar optimization has been made so far, and that is where const enum can be executed inline, that's all. With regard to this type of optimization, type-based optimization is also mentioned at the end of the previous article, but for now, TS does not have this feature. If such simple optimizations are not supported, it is even less possible for more complex metaprogramming (which requires logical derivation of generic parameters and eventually inline to where they are used).
That's all about C++ templates, after all, this is not an article about template metaprogramming, and I'm not an expert. For more questions about templates, you can ask Brother Wheel. Speaking of so many templates, I mainly want to say that generics and templates in TS are very different! If you moved from C++ or Java to the front end, you still need to re-recognize generics in TS.
2. Generics
I think generics in TS have three main uses:
Declare a generic container or component. For example, various container classes such as Map, Array, Set, etc.; various components, such as React.Component.
Constrain the type. For example, using extends constraints to pass in parameters conforms to a particular structure.
Generate a new type
With regard to the second and third points, because they have been mentioned very clearly in the previous article, I will not repeat them here. With regard to the first point, I would like to give two examples:
The first example is about generic containers. If I want to implement a simple generic linked list, the code is as follows:
Class LinkedList {/ / generic class value: t; next?: LinkedList; / / can use itself to declare constructor (value: t, next?: LinkedList) {this.value = value; this.next = next;} log () {if (this.next) {this.next.log ();} console.log (this.value);}} let list: LinkedList / / specialize generics to number [1,2,3] .forEach (value = > {list = new LinkedList (value, list);}); list.log (); / / 1 2 3
The second is a generic component, and if I want to implement a generic form component, I can write this:
Function Form ({data}: {data: t}) {return ({data.map ((value, key) = >)})}
This example demonstrates not only generic components, but also how to define generic constraints using extends. The real-world generic form component may be more complex than this, and the above is just a demonstration of ideas.
That's it, TS's generics are over! But this article is not over, so let's take a look at some advanced techniques for using generics.
Third, generic recursion
To put it simply, recursion is a kind of problem-solving idea that the output of the function can continue to be used as input for logical calculus. To give a simple example, for example, if we want to calculate addition, we define an add function, which can only find the sum of two numbers, but now we have three numbers such as 1, 2, and 3, which need to be calculated, so how can we use the existing tools to solve this problem? The answer is simple. First, add (1,2) is 3, and then add (3,3) is 6. This is the idea of recursion.
In real life, recursion is so common that we often ignore its existence. The same is true of the world of programs. Here is an example and use this example to illustrate how recursion in TS is implemented. For example, I now have a generic type ReturnType, which returns the return type of a function. But now I have a function with a deep call level, and I don't know how deep it is. What should I do?
Idea 1:
Type DeepReturnType any > = ReturnType extends (... args: any) = > any? DeepReturnType / / reference itself here: ReturnType
The description of the above code: here defines a generic type of DeepReturnType, the type constraint is to accept any parameter, return any type of function. If its return type is a function, continue to call itself with the return type, otherwise the return type of the function is returned.
There is a but behind any intuitive and concise solution. However, this cannot be compiled. The main reason is that TS does not support it temporarily. I don't know whether to support it in the future, but the official reason is very clear:
This circular intention cannot constitute an object graph unless you postpone it in some way (through inertia or state).
There is really no way to know if the type derivation is over.
We can use finite types of recursion in the compiler, but the question is not whether the type terminates, but how computationally intensive and memory allocation laws are.
A meta-question: do we want people to write code like this? This usage scenario exists, but the type of implementation is not necessarily suitable for the consumer of the library.
Conclusion: we are not ready for this kind of thing.
So, how can we achieve this kind of demand? There are ways, such as the official idea, we can use a limited number of recursions. Here are my ideas:
/ / two-layer generic type type ReturnType1 any > = ReturnType extends (... args: any) = > any? ReturnType: ReturnType;// three-layer generic type type ReturnType2 any > = ReturnType extends (... args: any) = > any? ReturnType1: ReturnType;// four-layer generic type, which can satisfy most cases type DeepReturnType any > = ReturnType extends (. Args: any) = > any? ReturnType2: ReturnType; / / Test const deep3Fn = () = > "flag is win" as const; / / four-tier function type Returned = DeepReturnType; / / type Returned = "flag is win" const deep1Fn = () = > "flag is win" as const; / / one-tier function type Returned = DeepReturnType; / / type Returned = "flag is win"
This technique can be extended to Exclude, Optional, or Required that define deep structures, and so on.
Default generic parameters
Sometimes we like generics very much, but sometimes we don't want consumers of classes or functions to specify the type of generics every time, so we can use the default generic parameters. This is widely used in many third-party libraries, such as:
/ / class Component {props: P; state: s; context:C.} / / you need to use class MyComponent extends Component {} / / but if my component is a pure component, you don't need props, state and context / / you can define class Component {props: P; state: s; context:C.} / / then you can use class MyComponent extends Component {}
I find this feature very useful because it implements partial instantiation in C++ templates in a natural way in js.
5. Generic overloading
Generic overloading has been mentioned in the official documentation, and this overloading depends on some mechanism of function overloading, so let's take a look at function overloading in TS first. Here, I'll use the map function in lodash as an example. The second argument of the map function can accept a string or function, such as the official website example:
Const square = (n) = > n * the mapmap of the string / receive function ({'averse: 4,' bounded: 8}, square); / / > [16,64] (iteration order is not guaranteed) const users = [{'user':' barney'}, {'user':' fred'}]; / / receive the mapmap of the string (users, 'user'); / / > [' barney', 'fred']
So how do you express such a type declaration in TS? I can use function overloading, such as this:
/ / only the demonstration is made here, and the correctness is not guaranteed. In a real scenario, the correct type needs to be filled instead of anyinterface MapFn {(obj: any, prop: string): any; / / when receiving string, scenario 1 (obj: any, fn: (value: any) = > any): any; / / when receiving function, scenario 2} const map: MapFn = () = > ({}); map (users, 'user') / / overload scenario 1 map ({'averse: 4,' baked: 8}, square); / / reload scenario 2
The above code uses a strange mechanism in TS, that is, the definition of functions, new and other functions can be written in interface. The main purpose of this feature is to support callable objects in js. For example, in jQuery, we can execute $("# banner-message") directly or call its method $.ajax ().
Of course, you can also use a more traditional approach, such as the following:
Function map (obj: any, prop: string): any;function map (obj: any, fn: (value: any) = > any): any;function map (obj, secondary): any {}
Here, it is basically clear that function overloading. When extended to generics, it's basically the same. Here is an example of a question raised by a bosom friend. I will not repeat it here. The solution goes something like this:
Interface FN {(obj: {value: string; onChange: () = > {}}): void; (obj: t): void; / /, for type T of obj, it never accepts other key. } const fn: FN = () = > {}; fn ({}); / / correct fn ({value: "Hi"}); / / error fn ({onChange: () = > {}}); / / error fn ({value: "Hi", onChange: () = > ({})}); / / so far, the study of "how to understand generics in C++ TpeScript series" 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.